1. Libraries and functions

1.1 Libraries

Load the required libraries.

library(tidyverse)
library(sf)
library(here)
library(readxl)
library(scales)
library(DT)
library(brms)
library(tidybayes)
library(patchwork)
library(marginaleffects)
library(ggrepel)
library(scico)
library(ggdensity)
library(ggpubr)
library(units)
#library(ggsn)

1.2 Helper functions

Functions that we will use throughout the script

#labeller for years
year_labels <- c(1950:1963)

#The Glasgow mass minuture chest X-ray campaign happened between 11th March and 12th April 1957
#Segment for graphs to match ACF period
acf_start <- decimal_date(ymd("1957-03-11"))
acf_end <- decimal_date(ymd("1957-04-12"))

Function for counterfactual plots



plot_counterfactual <- function(model_data, model, population_denominator, outcome, grouping_var=NULL, re_formula,...){
  
  #labeller for years
  year_labels <- c(1950:1963)

  #The Glasgow mass minuture chest X-ray campaign happened between 11th March and 12th April 1957
  #Segment for graphs to match ACF period
  acf_start <- decimal_date(ymd("1957-03-11"))
  acf_end <- decimal_date(ymd("1957-04-12"))

  summary <- {{model_data}} %>%
    select(year, year2, y_num, acf_period, {{population_denominator}}, {{outcome}}, {{grouping_var}}) %>%
    add_epred_draws({{model}}, re_formula={{re_formula}}) %>%
    group_by(year2, acf_period, {{grouping_var}}) %>%
    mean_qi() %>%
    mutate(.epred_inc = .epred/{{population_denominator}}*100000,
          .epred_inc.lower = .epred.lower/{{population_denominator}}*100000,
          .epred_inc.upper = .epred.upper/{{population_denominator}}*100000) %>%
    mutate(acf_period = case_when(acf_period=="a. pre-acf" ~ "Before Intervention",
                                  acf_period=="c. post-acf" ~ "Post Intervention"))



  #create the counterfactual (no intervention), and summarise
  
  counterfact <-
    add_epred_draws(object = {{model}},
                    newdata = {{model_data}} %>%
                                  select(year, year2, y_num, {{population_denominator}}, {{grouping_var}}, {{outcome}}) %>%
                                  mutate(acf_period = "a. pre-acf"), re_formula={{re_formula}}) %>%
    group_by(year2, acf_period, {{grouping_var}}) %>%
    mean_qi() %>%
    mutate(.epred_inc = .epred/{{population_denominator}}*100000,
         .epred_inc.lower = .epred.lower/{{population_denominator}}*100000,
         .epred_inc.upper = .epred.upper/{{population_denominator}}*100000) %>%
    mutate(acf_period = case_when(acf_period=="a. pre-acf" ~ "Before Intervention",
                                acf_period=="c. post-acf" ~ "Post Intervention"))
  


  #plot the intervention effect
p <- summary %>%
    droplevels() %>%
    ggplot() +
    geom_ribbon(aes(ymin=.epred_inc.lower, ymax=.epred_inc.upper, x=year2, group = acf_period, fill=acf_period), alpha=0.5) +
    geom_ribbon(data = counterfact %>% filter(year>=1956), 
                aes(ymin=.epred_inc.lower, ymax=.epred_inc.upper, x=year2, fill="Counterfactual"), alpha=0.5) +
    geom_line(data = counterfact %>% filter(year>=1956), 
              aes(y=.epred_inc, x=year2, colour="Counterfactual")) +
    geom_line(aes(y=.epred_inc, x=year2, group=acf_period,  colour=acf_period)) +
    geom_point(data = {{model_data}}, aes(y={{outcome}}, x=year2, shape=acf_period), size=2) +
    geom_vline(aes(xintercept=acf_end), linetype=3) +
    theme_ggdist() +
    scale_y_continuous(labels=comma, limits = c(0,NA)) +
    scale_x_continuous(labels = year_labels,
                       breaks = year_labels) +
    scale_fill_manual(values = c("#DE0D92", "grey50", "#4D6CFA") , name="", na.translate = F) +
    scale_colour_manual(values = c("#DE0D92", "grey50", "#4D6CFA") , name="", na.translate = F) +
    scale_shape_discrete(name="", na.translate = F) +
    labs(
      x = "Year",
      y = "Case notification rate (per 100,000)",
      caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
    ) +
    theme(legend.position = "bottom",
          panel.border = element_rect(colour = "grey78", fill=NA),
          text = element_text(size=8),
          axis.text.x = element_text(size=8, angle = 90, hjust=1, vjust=0.5),
          legend.text = element_text(size=8)) +
    guides(shape="none")

    facet_vars <- vars(...)

  if (length(facet_vars) != 0) {
    p <- p + facet_wrap(facet_vars)
  }
  p

}

Function for calculating measures of change over time (RR.peak, RR.level, RR.slope)


summarise_change <- function(model_data, model, population_denominator, grouping_var = NULL, re_formula = NULL) {
  
  #functions for calculating RR.peak
  #i.e. relative case notification rate in 1957 vs. counterfactual trend for 1957
  
  grouping_var <- enquo(grouping_var)
  
  if (!is.null({{grouping_var}})) {
    
    #make the prediction matrix, conditional on whether we want random effects included or not.
    out <- crossing({{model_data}} %>% 
                      select({{population_denominator}}, y_num, !!grouping_var) %>%
                      filter(y_num == 8),
                    acf_period = c("a. pre-acf", "b. acf")
    )
  } else {
    
    out <- crossing({{model_data}} %>% 
                      select({{population_denominator}}, y_num) %>%
                      filter(y_num == 8),
                    acf_period = c("a. pre-acf", "b. acf")
    )
  }
  
  peak_draws <- add_epred_draws(newdata = out,
                  object = {{model}},
                  re_formula = {{re_formula}}) %>%
    mutate(epred_cnr = .epred/population_without_inst_ship*100000) %>%
    group_by(.draw, !!grouping_var) %>%
    summarise(estimate = last(epred_cnr)/first(epred_cnr)) %>%
    ungroup() %>%
    mutate(measure = "RR.peak")
  
  peak_summary <- peak_draws %>%
    group_by(!!grouping_var) %>%
    mean_qi(estimate) %>%
    mutate(measure = "RR.peak")
  
  
  #functions for calculating RR.step
  #i.e. relative case notification rate in 1958 vs. counterfactual trend for 1958
  
    if (!is.null({{grouping_var}})) {
    out2 <- crossing({{model_data}} %>% 
                      select({{population_denominator}}, y_num, !!grouping_var) %>%
                      filter(y_num == 9),
                    acf_period = c("a. pre-acf", "c. post-acf")
    )
  } else {
    
    out2 <- crossing({{model_data}} %>% 
                      select({{population_denominator}}, y_num) %>%
                      filter(y_num == 9),
                    acf_period = c("a. pre-acf", "c. post-acf")
    )
  }
  
    level_draws <- add_epred_draws(newdata = out2,
                  object = {{model}},
                  re_formula = {{re_formula}}) %>%
    arrange(y_num, .draw) %>%
    mutate(epred_cnr = .epred/population_without_inst_ship*100000) %>%
    group_by(.draw, !!grouping_var) %>%
    summarise(estimate = last(epred_cnr)/first(epred_cnr)) %>%
    ungroup() %>%
    mutate(measure = "RR.level")
  
  level_summary <- level_draws %>%
    group_by(!!grouping_var) %>%
    mean_qi(estimate) %>%
    mutate(measure = "RR.level")
    
    
  #functions for calculating RR.slope
  #i.e. relative change in case notification rate in 1958-1963 vs. counterfactual trend for 1959-1963
  
    if (!is.null({{grouping_var}})) {
    out3 <- crossing({{model_data}} %>% 
                      select({{population_denominator}}, y_num, !!grouping_var) %>%
                      filter(y_num %in% c(9,14)),
                    acf_period = c("a. pre-acf", "c. post-acf")
    )
  } else {
    
    out3 <- crossing({{model_data}} %>% 
                      select({{population_denominator}}, y_num) %>%
                      filter(y_num %in% c(9,14)),
                    acf_period = c("a. pre-acf", "c. post-acf")
    )
  }
  
    slope_draws <- add_epred_draws(newdata = out3,
                  object = {{model}},
                  re_formula = {{re_formula}}) %>%
        arrange(y_num) %>%
        ungroup() %>%
        mutate(epred_cnr = .epred/population_without_inst_ship*100000) %>%
        group_by(.draw, acf_period, !!grouping_var) %>%
        summarise(slope = (last(epred_cnr) - first(epred_cnr)) / (last(y_num)-first(y_num))) %>%
        ungroup() %>%
        group_by(.draw, !!grouping_var) %>%
        summarise(estimate = last(slope)/first(slope)) %>%
        mutate(measure = "RR.slope")
  
  slope_summary <- slope_draws %>%
     group_by(!!grouping_var) %>%
      median_qi(estimate) %>%
      mutate(measure = "RR.slope")
    
  #gather all the results into a named list
    lst(peak_draws=peak_draws, peak_summary=peak_summary, 
        level_draws=level_draws, level_summary=level_summary, 
        slope_draws=slope_draws, slope_summary=slope_summary)
  
}

Function for calculating difference from counterfactual


calculate_counterfactual <- function(model_data, model, population_denominator, grouping_var=NULL, re_formula=NA){
  
  #effect vs. counterfactual
  counterfact <-
      add_epred_draws(object = {{model}},
                      newdata = {{model_data}} %>%
                                    select(year, year2, y_num, {{population_denominator}}, {{grouping_var}}) %>%
                                    mutate(acf_period = "a. pre-acf"),
                      re_formula = {{re_formula}}) %>%
      group_by(.draw, year, {{grouping_var}}, acf_period) %>%
      mutate(.epred_inc_counterf = .epred/{{population_denominator}}*100000, .epred_counterf=.epred)  %>%
      filter(year>1957) %>%
      ungroup() %>%
      select(year, {{population_denominator}}, .draw, .epred_counterf, .epred_inc_counterf, {{grouping_var}})
  
  #Calcuate case notification rate per draw, then summarise.
  post_change <-
      add_epred_draws(object = {{model}},
                      newdata = {{model_data}} %>%
                                    select(year, year2, y_num, {{population_denominator}}, {{grouping_var}}, acf_period),
                      re_formula = {{re_formula}}) %>%
      group_by(.draw, year, {{grouping_var}}, acf_period) %>%
      mutate(.epred_inc = .epred/{{population_denominator}}*100000)  %>%
      filter(year>1957) %>%
      ungroup() %>%
      select(year, {{population_denominator}}, {{grouping_var}}, .draw, .epred, .epred_inc, {{grouping_var}}) 
  
  #for the overall period
    counterfact_overall <-
      add_epred_draws(object = {{model}},
                      newdata = {{model_data}} %>%
                                    select(year, year2, y_num, {{population_denominator}}, {{grouping_var}}) %>%
                                    mutate(acf_period = "a. pre-acf"),
                      re_formula = {{re_formula}}) %>%
      group_by(.draw, {{grouping_var}}) %>%
      filter(year>1957) %>%
      ungroup() %>%
      select({{population_denominator}}, .draw, .epred, {{grouping_var}})  %>%
      group_by(.draw, {{grouping_var}}) %>%
      summarise(.epred_counterf = sum(.epred)) 
  
  #Calcuate case notification rate per draw, then summarise.
  post_change_overall <-
      add_epred_draws(object = {{model}},
                      newdata = {{model_data}} %>%
                                    select(year, year2, y_num, {{population_denominator}}, {{grouping_var}}, acf_period),
                      re_formula = {{re_formula}}) %>%
      group_by(.draw, {{grouping_var}}) %>%
      filter(year>1957) %>%
      ungroup() %>%
      select({{population_denominator}}, {{grouping_var}}, .draw, .epred) %>%
      group_by(.draw, {{grouping_var}}) %>%
      summarise(.epred = sum(.epred)) 
  
  
counter_post <-
  left_join(counterfact, post_change) %>%
    mutate(cases_averted = .epred_counterf-.epred,
           pct_change = (.epred - .epred_counterf)/.epred_counterf,
           diff_inc100k = .epred_inc - .epred_inc_counterf,
           rr_inc100k = .epred_inc/.epred_inc_counterf) %>%
    group_by(year, {{grouping_var}}) %>%
    mean_qi(cases_averted, pct_change, diff_inc100k, rr_inc100k) %>%
    ungroup()

counter_post_overall <-
  left_join(counterfact_overall, post_change_overall) %>%
    mutate(cases_averted = .epred_counterf-.epred,
           pct_change = (.epred - .epred_counterf)/.epred_counterf) %>%
    group_by({{grouping_var}}) %>%
    mean_qi(cases_averted, pct_change) %>%
    ungroup()

lst(counter_post, counter_post_overall)

}

Function for tidying up counterfactuals (mostly for making nice tables)


tidy_counterfactuals <- function(data){
  data %>%
  mutate(across(c(cases_averted:cases_averted.upper, diff_inc100k:diff_inc100k.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(rr_inc100k:rr_inc100k.upper), number_format(accuracy = 0.01))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  mutate(year = as.character(year),
            cases_averted = glue::glue("{cases_averted} ({cases_averted.lower} to {cases_averted.upper})"),
            pct_change = glue::glue("{pct_change} ({pct_change.lower} to {pct_change.upper})"),
            diff_inc = glue::glue("{diff_inc100k} ({diff_inc100k.lower} to {diff_inc100k.upper})"),
            rr_inc = glue::glue("{rr_inc100k} ({rr_inc100k.lower} to {rr_inc100k.upper})"))
}


tidy_counterfactuals_overall <- function(data){
  data %>%
  mutate(across(c(cases_averted:cases_averted.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  mutate(year = as.character(year),
            cases_averted = glue::glue("{cases_averted} ({cases_averted.lower} to {cases_averted.upper})"),
            pct_change = glue::glue("{pct_change} ({pct_change.lower} to {pct_change.upper})"))
}

2. Data

Import datasets for analysis

2.1 Shapefiles

Make a map of Glasgow wards


glasgow_wards_1951 <- st_read(here("mapping/glasgow_wards_1951.geojson"))
Reading layer `glasgow_wards_1951' from data source 
  `/Users/petermacpherson/Dropbox/Projects/Historical TB ACF 2023-11-28/Work/analysis/glasgow-cxr/mapping/glasgow_wards_1951.geojson' 
  using driver `GeoJSON'
Simple feature collection with 37 features and 3 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -4.393502 ymin: 55.77464 xmax: -4.070411 ymax: 55.92814
Geodetic CRS:  WGS 84

#read in Scotland boundary
scotland <- st_read(here("mapping/Scotland_boundary/Scotland boundary.shp"))
Reading layer `Scotland boundary' from data source 
  `/Users/petermacpherson/Dropbox/Projects/Historical TB ACF 2023-11-28/Work/analysis/glasgow-cxr/mapping/Scotland_boundary/Scotland boundary.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 1 feature and 1 field
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 5513 ymin: 530249 xmax: 470332 ymax: 1220302
Projected CRS: OSGB36 / British National Grid
#make a bounding box for Glasgow
bbox <- st_bbox(glasgow_wards_1951) |> st_as_sfc()

#plot scotland with a bounding box around the City of Glasgow
scotland_with_bbox <- ggplot() +
  geom_sf(data = scotland, fill="antiquewhite") +
  geom_sf(data = bbox, colour = "#C60C30", fill="antiquewhite") +
  theme_void() +
  theme(panel.border = element_rect(colour = "grey78", fill=NA, linewidth = 0.5),
        panel.background = element_rect(fill = "#EAF7FA", size = 0.3))
Warning: The `size` argument of `element_rect()` is deprecated as of ggplot2 3.4.0.
Please use the `linewidth` argument instead.
#plot the wards
#note we tidy up some names to fit on map
glasgow_ward_map <- glasgow_wards_1951 %>%
  mutate(ward = case_when(ward=="Shettleston and Tollcross" ~ "Shettleston and\nTollcross",
                          ward=="Partick (West)" ~ "Partick\n(West)",
                          ward=="Partick (East)" ~ "Partick\n(East)",
                          ward=="North Kelvin" ~ "North\nKelvin",
                          ward=="Kinning Park" ~ "Kinning\nPark",
                          TRUE ~ ward)) %>%
  
  ggplot() +
  geom_sf(aes(fill=division)) +
  geom_sf_label(aes(label = ward), size=3, fill=NA, label.size = NA, colour="black") +
  #scale_colour_identity() +
  scale_fill_brewer(palette = "Set3", name="City of Glasgow Division") +
  theme_grey() +
  labs(x="",
       y="",
       fill="Division") +
  theme(legend.position = "top",
        
        panel.border = element_rect(colour = "grey78", fill=NA, linewidth = 0.5),
        panel.background = element_rect(fill = "antiquewhite", size = 0.3),
        panel.grid.major = element_line(color = "grey78")) +
  guides(fill=guide_legend(title.position = "top", title.hjust = 0.5, title.theme = element_text(face="bold")))

#add the map of scotland as an inset
glasgow_ward_map + inset_element(scotland_with_bbox, 0.75, 0, 1, 0.4)

ggsave(here("figures/s1.png"), height=10, width = 12)

NA
NA

Calculate areas per geographical unit

sf_use_s2(FALSE) #https://github.com/r-spatial/sf/issues/1762
Spherical geometry (s2) switched off
glasgow_wards_1951 <- glasgow_wards_1951 %>%
  mutate(area = st_area(glasgow_wards_1951))


glasgow_wards_1951$area_km <- units::set_units(glasgow_wards_1951$area, km^2)

Make division shape files, and calculate area (stopped working, need to fix!)


# glasgow_divisions_1951 <- glasgow_wards_1951 %>%
#   group_by(division) %>% 
#   summarize(geometry = st_union(geometry)) %>%
#   nngeo::st_remove_holes() %>%
#   mutate(area = st_area(glasgow_divisions_1951))
# 
# glasgow_divisions_1951$area_km <- units::set_units(glasgow_divisions_1951$area, km^2)

3. Denominators

Load in the datasets for denonomiators, and check for consistency.


overall_pops <- read_xlsx(path = "2023-11-28_glasgow-acf.xlsx", sheet = "overall_population")

overall_pops %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()

#shift year to midpoint
overall_pops <- overall_pops %>%
  mutate(year2 = year+0.5)

Note, we have three population estimates:

  1. Population without institutionalised people or people in shipping
  2. Population in institutions
  3. Population in shipping

(Population in shipping is estimated from the 1951 census, so is the same for most years)

3.1 Overall population

First, plot the total population


overall_pops %>%
  ggplot() +
  geom_area(aes(y=total_population, x=year2), alpha=0.5, colour = "mediumseagreen", fill="mediumseagreen") +
  geom_point(aes(y=total_population, x=year2), colour = "mediumseagreen") +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels) +
  labs(
    title = "Glasgow Corporation: total population",
    subtitle = "1950 to 1963",
    x = "Year",
    y = "Population",
    caption = "Mid-year estimates\nMass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme_ggdist()

NA
NA

Now the population excluding institutionalised and shipping population


overall_pops %>%
  ggplot() +
  geom_area(aes(y=population_without_inst_ship, x=year2), alpha=0.5, colour = "purple", fill="purple") +
  geom_point(aes(y=population_without_inst_ship, x=year2), colour = "purple") +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels) +
  labs(
    title = "Glasgow Corporation: population excluding institutionalised and shipping",
    subtitle = "1950 to 1963",
    x = "Year",
    y = "Population",
    caption = "Mid-year estimates\nMass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme_ggdist()

NA
NA

3.2 Population by Ward

There are 5 Divisions containing 37 Wards in the Glasgow Corporation, with consistent boundaries over time.

#look-up table for divisions and wards
ward_lookup <- read_xlsx(path = "2023-11-28_glasgow-acf.xlsx", sheet = "divisions_wards")


ward_pops <- read_xlsx(path = "2023-11-28_glasgow-acf.xlsx", sheet = "ward_population")

ward_pops %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()

#shift year to midpoint
ward_pops <- ward_pops %>%
  mutate(year2 = year+0.5)

#Get the Division population
division_pops <- ward_pops %>%
  group_by(division, year) %>%
  summarise(population_without_inst_ship = sum(population_without_inst_ship, na.rm = TRUE),
            institutions = sum(institutions, na.rm = TRUE),
            shipping = sum(shipping, na.rm = TRUE),
            total_population = sum(total_population, na.rm = TRUE))
`summarise()` has grouped output by 'division'. You can override using the `.groups` argument.
division_pops %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()
NA

Plot the overall population by Division and Ward


division_pops %>%
  mutate(year2 = year+0.5) %>%
  ggplot() +
  geom_area(aes(y=total_population, x=year2, colour=division, fill=division), alpha=0.8) +
  geom_point(aes(y=total_population, x=year2, colour=division)) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  facet_wrap(division~.) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  scale_fill_brewer(palette = "Set3", name = "") +
  scale_colour_brewer(palette = "Set3", name = "") +
  labs(
    title = "Glasgow Corporation: total population by Division",
    subtitle = "1950 to 1963",
    x = "Year",
    y = "Population",
    caption = "Mid-year estimates\nMass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme_ggdist() +
  theme(legend.position = "bottom")

NA
NA

ward_pops %>%
  ggplot() +
  geom_area(aes(y=total_population, x=year2, colour=division, fill=division), alpha=0.8) +
  geom_point(aes(y=total_population, x=year2, colour=division)) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  facet_wrap(ward~., ncol=6) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  scale_fill_brewer(palette = "Set3", name="Division") +
  scale_colour_brewer(palette = "Set3", name = "Division") +
  labs(
    title = "Glasgow City: total population by Ward",
    subtitle = "1950 to 1963",
    x = "Year",
    y = "Population",
    caption = "Mid-year estimates\nMass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme_ggdist() +
  theme(legend.position = "bottom")

ggsave(here("figures/s2.png"), height=10, width=12)

Approximately, how many person-years of follow-up do we have?


overall_pops %>%
  ungroup() %>%
  summarise(across(year, length, .names = "years"),
            across(c(population_without_inst_ship, total_population), sum)) %>%
  mutate(across(where(is.double), comma)) %>%
  datatable()
NA
NA

Change in population by ward


ward_pops %>%
  group_by(ward) %>%
  summarise(pct_change_pop = (last(population_without_inst_ship) - first(population_without_inst_ship))/first(population_without_inst_ship)) %>%
  mutate(pct_change_pop = percent(pct_change_pop)) %>%
  arrange(pct_change_pop) %>%
  datatable()
NA
NA
NA

Output population density by ward and divison for regression modelling

Wards first

(stopped working, need to fix)


# ward_covariates <-  glasgow_wards_1951 %>%
#   left_join(ward_pops) %>%
#   mutate(people_per_km_sq = as.double(population_without_inst_ship/area_km))
# 
# #plot it out
# 
# ward_covariates %>%
#   ggplot() +
#   geom_sf(aes(fill=people_per_km_sq)) + 
#   facet_wrap(year~., ncol=7) +
#   scale_fill_viridis_c(option="A") +
#   theme(legend.position = "bottom",
#         axis.text.x = element_text(angle = 45, hjust=1))
# 
# ggsave(here("figures/ward_pop_density.png"), width=10)
# 
# write_rds(ward_covariates, here("populations/ward_covariates.rds"))

Now divisions first

(stopped working, need to fix)


# division_covariates <-  glasgow_divisions_1951 %>%
#   left_join(division_pops) %>%
#   mutate(people_per_km_sq = as.double(total_population/area_km))
# 
# #plot it out
# 
# division_covariates %>%
#   ggplot() +
#   geom_sf(aes(fill=people_per_km_sq)) + 
#   geom_sf_label(aes(label = division), size=3, fill=NA, label.size = NA, colour="black", family = "Segoe UI") +
#   facet_wrap(year~., ncol=7) +
#   scale_fill_viridis_c(option="G") +
#   theme(legend.position = "bottom",
#         axis.text.x = element_text(angle = 45, hjust=1))
# 
# ggsave(here("figures/division_pop_density.png"), width=10)
# 
# write_rds(division_covariates, here("populations/division_covariates.rds"))

3.3 Population by age and sex


age_sex <- read_xlsx(path = "2023-11-28_glasgow-acf.xlsx", sheet = "age_sex_population") %>%
  pivot_longer(cols = c(male, female),
               names_to = "sex")

#collapse down to smaller age groups to be manageable
age_sex <- age_sex %>%
  ungroup() %>%
  mutate(age = case_when(age == "0 to 4" ~ "00 to 04",
                         age == "5 to 9" ~ "05 to 14",
                         age == "10 to 14" ~ "05 to 14",
                         age == "15 to 19" ~ "15 to 24",
                         age == "20 to 24" ~ "15 to 24",
                         age == "25 to 29" ~ "25 to 34",
                         age == "30 to 34" ~ "25 to 34",
                         age == "35 to 39" ~ "35 to 44",
                         age == "40 to 44" ~ "35 to 44",
                         age == "45 to 49" ~ "45 to 59",
                         age == "50 to 54" ~ "45 to 59",
                         age == "55 to 59" ~ "45 to 59",
                         TRUE ~ "60 & up")) %>%
  group_by(year, age, sex) %>%
  mutate(value = sum(value)) %>%
  ungroup()



m_age_sex <- lm(value ~ splines::ns(year, knots = 3)*age*sex, data = age_sex)

summary(m_age_sex)
Warning: essentially perfect fit: summary may be unreliable

Call:
lm(formula = value ~ splines::ns(year, knots = 3) * age * sex, 
    data = age_sex)

Residuals:
       Min         1Q     Median         3Q        Max 
-2.107e-10 -7.560e-13  0.000e+00  0.000e+00  2.107e-10 

Coefficients: (14 not defined because of singularities)
                                                    Estimate Std. Error    t value Pr(>|t|)
(Intercept)                                        5.222e+04  3.820e-10  1.367e+14   <2e-16
splines::ns(year, knots = 3)1                     -8.043e+03  7.621e-10 -1.055e+13   <2e-16
splines::ns(year, knots = 3)2                             NA         NA         NA       NA
age05 to 14                                        3.669e+04  4.679e-10  7.843e+13   <2e-16
age15 to 24                                       -3.893e+03  4.679e-10 -8.320e+12   <2e-16
age25 to 34                                       -3.996e+04  4.679e-10 -8.540e+13   <2e-16
age35 to 44                                       -4.230e+04  4.679e-10 -9.040e+13   <2e-16
age45 to 59                                        5.459e+04  4.411e-10  1.238e+14   <2e-16
age60 & up                                         7.533e+04  4.126e-10  1.826e+14   <2e-16
sexmale                                            3.374e+03  5.402e-10  6.244e+12   <2e-16
splines::ns(year, knots = 3)1:age05 to 14         -1.863e+03  9.334e-10 -1.996e+12   <2e-16
splines::ns(year, knots = 3)2:age05 to 14                 NA         NA         NA       NA
splines::ns(year, knots = 3)1:age15 to 24          7.533e+04  9.334e-10  8.070e+13   <2e-16
splines::ns(year, knots = 3)2:age15 to 24                 NA         NA         NA       NA
splines::ns(year, knots = 3)1:age25 to 34          1.325e+05  9.334e-10  1.420e+14   <2e-16
splines::ns(year, knots = 3)2:age25 to 34                 NA         NA         NA       NA
splines::ns(year, knots = 3)1:age35 to 44          1.380e+05  9.334e-10  1.479e+14   <2e-16
splines::ns(year, knots = 3)2:age35 to 44                 NA         NA         NA       NA
splines::ns(year, knots = 3)1:age45 to 59          3.474e+03  8.800e-10  3.948e+12   <2e-16
splines::ns(year, knots = 3)2:age45 to 59                 NA         NA         NA       NA
splines::ns(year, knots = 3)1:age60 & up          -8.453e+04  8.232e-10 -1.027e+14   <2e-16
splines::ns(year, knots = 3)2:age60 & up                  NA         NA         NA       NA
splines::ns(year, knots = 3)1:sexmale             -1.994e+03  1.078e-09 -1.850e+12   <2e-16
splines::ns(year, knots = 3)2:sexmale                     NA         NA         NA       NA
age05 to 14:sexmale                                1.053e+04  6.617e-10  1.592e+13   <2e-16
age15 to 24:sexmale                                2.352e+04  6.617e-10  3.555e+13   <2e-16
age25 to 34:sexmale                                1.355e+04  6.617e-10  2.047e+13   <2e-16
age35 to 44:sexmale                               -1.727e+03  6.617e-10 -2.611e+12   <2e-16
age45 to 59:sexmale                                2.774e+03  6.238e-10  4.446e+12   <2e-16
age60 & up:sexmale                                -7.761e+04  5.835e-10 -1.330e+14   <2e-16
splines::ns(year, knots = 3)1:age05 to 14:sexmale -2.049e+04  1.320e-09 -1.552e+13   <2e-16
splines::ns(year, knots = 3)2:age05 to 14:sexmale         NA         NA         NA       NA
splines::ns(year, knots = 3)1:age15 to 24:sexmale -6.780e+04  1.320e-09 -5.136e+13   <2e-16
splines::ns(year, knots = 3)2:age15 to 24:sexmale         NA         NA         NA       NA
splines::ns(year, knots = 3)1:age25 to 34:sexmale -3.804e+04  1.320e-09 -2.882e+13   <2e-16
splines::ns(year, knots = 3)2:age25 to 34:sexmale         NA         NA         NA       NA
splines::ns(year, knots = 3)1:age35 to 44:sexmale -1.171e+04  1.320e-09 -8.874e+12   <2e-16
splines::ns(year, knots = 3)2:age35 to 44:sexmale         NA         NA         NA       NA
splines::ns(year, knots = 3)1:age45 to 59:sexmale -3.473e+04  1.244e-09 -2.791e+13   <2e-16
splines::ns(year, knots = 3)2:age45 to 59:sexmale         NA         NA         NA       NA
splines::ns(year, knots = 3)1:age60 & up:sexmale   1.056e+05  1.164e-09  9.071e+13   <2e-16
splines::ns(year, knots = 3)2:age60 & up:sexmale          NA         NA         NA       NA
                                                     
(Intercept)                                       ***
splines::ns(year, knots = 3)1                     ***
splines::ns(year, knots = 3)2                        
age05 to 14                                       ***
age15 to 24                                       ***
age25 to 34                                       ***
age35 to 44                                       ***
age45 to 59                                       ***
age60 & up                                        ***
sexmale                                           ***
splines::ns(year, knots = 3)1:age05 to 14         ***
splines::ns(year, knots = 3)2:age05 to 14            
splines::ns(year, knots = 3)1:age15 to 24         ***
splines::ns(year, knots = 3)2:age15 to 24            
splines::ns(year, knots = 3)1:age25 to 34         ***
splines::ns(year, knots = 3)2:age25 to 34            
splines::ns(year, knots = 3)1:age35 to 44         ***
splines::ns(year, knots = 3)2:age35 to 44            
splines::ns(year, knots = 3)1:age45 to 59         ***
splines::ns(year, knots = 3)2:age45 to 59            
splines::ns(year, knots = 3)1:age60 & up          ***
splines::ns(year, knots = 3)2:age60 & up             
splines::ns(year, knots = 3)1:sexmale             ***
splines::ns(year, knots = 3)2:sexmale                
age05 to 14:sexmale                               ***
age15 to 24:sexmale                               ***
age25 to 34:sexmale                               ***
age35 to 44:sexmale                               ***
age45 to 59:sexmale                               ***
age60 & up:sexmale                                ***
splines::ns(year, knots = 3)1:age05 to 14:sexmale ***
splines::ns(year, knots = 3)2:age05 to 14:sexmale    
splines::ns(year, knots = 3)1:age15 to 24:sexmale ***
splines::ns(year, knots = 3)2:age15 to 24:sexmale    
splines::ns(year, knots = 3)1:age25 to 34:sexmale ***
splines::ns(year, knots = 3)2:age25 to 34:sexmale    
splines::ns(year, knots = 3)1:age35 to 44:sexmale ***
splines::ns(year, knots = 3)2:age35 to 44:sexmale    
splines::ns(year, knots = 3)1:age45 to 59:sexmale ***
splines::ns(year, knots = 3)2:age45 to 59:sexmale    
splines::ns(year, knots = 3)1:age60 & up:sexmale  ***
splines::ns(year, knots = 3)2:age60 & up:sexmale     
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 5.755e-11 on 44 degrees of freedom
Multiple R-squared:      1, Adjusted R-squared:      1 
F-statistic: 1.714e+29 on 27 and 44 DF,  p-value: < 2.2e-16
age_levels <- age_sex %>% select(age) %>% distinct() %>% pull() 

age_sex_nd <- 
  crossing(
    age=age_levels,
    sex=c("male", "female"),
    year = 1950:1963
  )

pred_pops <- age_sex_nd %>% modelr::add_predictions(m_age_sex)
Warning: prediction from rank-deficient fit; attr(*, "non-estim") has doubtful cases
pred_pops %>%
  ggplot(aes(x=year, y=pred, colour=age)) +
  geom_line() +
  geom_point() +
  facet_grid(sex~.) +
  scale_y_continuous(labels = comma, limits = c(0, 125000))


#How well do they match up with our overall populations?
pred_pops %>%
  group_by(year) %>%
  summarise(sum_pred_pop = sum(pred)) %>%
  right_join(overall_pops) %>%
  select(year, sum_pred_pop, population_without_inst_ship, total_population) %>%
  pivot_longer(cols = c(sum_pred_pop, population_without_inst_ship, total_population)) %>%
  ggplot(aes(x=year, y=value, colour=name)) +
  geom_point() +
  scale_y_continuous(labels = comma, limits = c(800000, 1250000))
Joining with `by = join_by(year)`

pred_pops %>%
  group_by(year, sex) %>%
  summarise(sum = sum(pred)) %>%
  group_by(year) %>%
  mutate(sex_ratio = first(sum)/last(sum))
`summarise()` has grouped output by 'year'. You can override using the `.groups` argument.

What percentage of adults (15+ participated in the intervention in 1957)?


pred_pops %>%
  ungroup() %>%
  filter(year==1957) %>%
  filter(age != "00 to 04",
         age != "05 to 14") %>%
  summarise(total_pop = sum(pred)) %>%
  mutate(cxr_screened = 622349) %>%
  mutate(pct_pop_cxr_screened = percent(cxr_screened/total_pop))

pred_pops %>%
  ungroup() %>%
  filter(year==1957) %>%
  filter(age != "00 to 04",
         age != "05 to 14") %>%
  summarise(total_pop = sum(pred), .by=sex) %>%
  mutate(cxr_screened = c(340474, 281875)) %>%
  mutate(pct_pop_cxr_screened = percent(cxr_screened/total_pop))
NA
NA

Population pyramids


label_abs <- function(x) {
  comma(abs(x))
}


pred_pops %>%
  ungroup() %>%
  group_by(year) %>%
  mutate(year_pop = sum(pred),
         age_sex_pct = percent(pred/year_pop, accuracy=0.1)) %>%
  mutate(sex = case_when(sex=="male" ~ "Male",
                         sex=="female" ~ "Female")) %>%
  ggplot(
    aes(x = age, fill = sex, 
        y = ifelse(test = sex == "Female",yes = -pred, no = pred))) + 
  geom_bar(stat = "identity") +
  geom_text(aes(label = age_sex_pct),
            position= position_stack(vjust=0.5), colour="white", size=2.5) +
  facet_wrap(year~., ncol=7) +
  coord_flip() +
  scale_y_continuous(labels = label_abs) +
  scale_fill_manual(values = c("mediumseagreen", "purple"), name="") +
  theme_ggdist() +
  theme(axis.text.x = element_text(angle=90, hjust = 1, vjust=0.5),
        legend.position = "bottom",
        panel.border = element_rect(colour = "grey78", fill=NA)) +
  labs(x="", y="") 


ggsave(here("figures/s3.png"), width=10)
Saving 10 x 4.51 in image

Not perfect, but resonably good. But ahhhhh… the age groups don’t align with the case notification age groups! Come back to think about this later.

4. Tuberculosis cases

Import the tuberculosis cases dataset

4.1 Overall notifications

Overall, by year.


cases_by_year <- read_xlsx("2023-11-28_glasgow-acf.xlsx", sheet = "by_year")

cases_by_year%>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()


#shift year to midpoint
cases_by_year <- cases_by_year %>%
  mutate(year2 = year+0.5)

Plot the overall number of case notified per year, by pulmonary and extra pulmonary classification.


cases_by_year %>%
  select(-total_notifications, -year) %>%
  pivot_longer(cols = c(pulmonary_notifications, `non-pulmonary_notifications`)) %>%
  mutate(name = case_when(name == "pulmonary_notifications" ~ "Pulmonary TB",
                          name == "non-pulmonary_notifications" ~ "Extra-pulmonary TB")) %>%
  ggplot() +
  geom_area(aes(y=value, x=year2, group = name, fill=name), alpha=0.5) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels) +
  scale_fill_brewer(palette = "Set1", name="") +
  labs(
    title = "Glasgow Corporation: Tuberculosis notifications",
    subtitle = "1950 to 1963, by TB disease classification",
    x = "Year",
    y = "Number of cases",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme_ggdist() +
  theme(legend.position = "bottom")

NA
NA

4.2 Notifications by Division

Read in the datasets and merge together.


#list all the sheets
all_sheets <- excel_sheets("2023-11-28_glasgow-acf.xlsx")

#get the ward sheets
ward_sheets <- enframe(all_sheets) %>%
  filter(grepl("by_ward", value)) %>%
  pull(value)


cases_by_ward_sex_year <- map_df(ward_sheets, ~read_xlsx(path = "2023-11-28_glasgow-acf.xlsx",
                                sheet = .))

cases_by_ward_sex_year %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()
NA

Aggregate together to get cases by division


cases_by_division <- cases_by_ward_sex_year %>%
  left_join(ward_lookup) %>%
  group_by(division, year, tb_type) %>%
  summarise(cases = sum(cases, na.rm = TRUE))
Joining with `by = join_by(ward)``summarise()` has grouped output by 'division', 'year'. You can override using the `.groups` argument.
#shift year to midpoint
cases_by_division <- cases_by_division %>%
  mutate(year2 = year+0.5) %>%
  ungroup()

cases_by_division  %>%
  select(-year2) %>%
  select(year, everything()) %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()


cases_by_division %>%
  mutate(tb_type = case_when(tb_type == "Pulmonary" ~ "Pulmonary TB",
                          tb_type == "Non-Pulmonary" ~ "Extra-pulmonary TB")) %>%
  ggplot() +
  geom_area(aes(y=cases, x=year2, group = tb_type, fill=tb_type), alpha=0.5) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  facet_wrap(division~., scales = "free_y") +
  scale_fill_brewer(palette = "Set1", name="") +
  labs(
    title = "Glasgow Corporation: Tuberculosis notifications by Division",
    subtitle = "1950 to 1963, by TB disease classification",
    x = "Year",
    y = "Number of cases",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)\nNote: extra-pulmonary TB cases by Division/Ward not reported in 1962-1963"
  ) +
  theme_ggdist() +
  theme(legend.position = "bottom")

4.3 Notifications by ward



cases_by_ward <- cases_by_ward_sex_year %>%
  group_by(ward, year, tb_type) %>%
  summarise(cases = sum(cases, na.rm = TRUE)) %>%
  ungroup()
`summarise()` has grouped output by 'ward', 'year'. You can override using the `.groups` argument.
cases_by_ward %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  select(year, everything()) %>%
  datatable()

#shift year to midpoint
cases_by_ward <- cases_by_ward %>%
  mutate(year2 = year+0.5)

cases_by_ward %>%
  mutate(tb_type = case_when(tb_type == "Pulmonary" ~ "Pulmonary TB",
                          tb_type == "Non-Pulmonary" ~ "Extra-pulmonary TB")) %>%
  ggplot() +
  geom_area(aes(y=cases, x=year2, group = tb_type, fill=tb_type), alpha=0.8) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  facet_wrap(ward~., scales = "free_y") +
  scale_fill_brewer(palette = "Set1", name="") +
  labs(
    title = "Glasgow Corporation: Tuberculosis notifications by Ward",
    subtitle = "1950 to 1963, by TB disease classification",
    x = "Year",
    y = "Number of cases",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)\nNote: extra-pulmonary TB cases by Division/Ward not reported in 1962-1963"
  ) +
  theme(legend.position = "bottom")

NA
NA

4.4 Notifications by age and sex

As we don’t have denominators, we will just model the change in counts.


#list all the sheets
all_sheets <- excel_sheets("2023-11-28_glasgow-acf.xlsx")

#get the ward sheets
age_sex_sheets <- enframe(all_sheets) %>%
  filter(grepl("by_age_sex", value)) %>%
  pull(value)


cases_by_age_sex <- map_df(age_sex_sheets, ~read_xlsx(path = "2023-11-28_glasgow-acf.xlsx",
                                sheet = .))

cases_by_age_sex %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()
NA
NA

5 TB case notification rates

5.1 Overall TB case notification rates

Now calculate case notification rates per 100,000 population

Merge the notification and population denominator datasets together.

Here we need to include the whole population (with shipping and institutions) as they are included in the notifications.


overall_inc <- overall_pops %>%
  left_join(cases_by_year)
Joining with `by = join_by(year, year2)`
overall_inc <- overall_inc %>%
  mutate(inc_pulm_100k = pulmonary_notifications/total_population*100000,
         inc_ep_100k = `non-pulmonary_notifications`/total_population*100000,
         inc_100k = total_notifications/total_population*100000)

overall_inc %>%
  select(year, inc_100k, inc_pulm_100k, inc_ep_100k) %>%
  mutate_at(.vars = vars(inc_100k, inc_pulm_100k, inc_ep_100k),
            .funs = funs(round)) %>%
  datatable()
Warning: `funs()` was deprecated in dplyr 0.8.0.
Please use a list of either functions or lambdas: 

  # Simple named list: 
  list(mean = mean, median = median)

  # Auto named with `tibble::lst()`: 
  tibble::lst(mean, median)

  # Using lambdas
  list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))

overall_inc %>%
  select(year2, inc_pulm_100k, inc_ep_100k) %>%
  pivot_longer(cols = c(inc_pulm_100k, `inc_ep_100k`)) %>%
  mutate(name = case_when(name == "inc_pulm_100k" ~ "Pulmonary TB",
                          name == "inc_ep_100k" ~ "Extra-pulmonary TB")) %>%
  ggplot() +
  geom_area(aes(y=value, x=year2, group = name, fill=name), alpha=0.5) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels) +
  scale_fill_brewer(palette = "Set1", name="") +
  labs(
    title = "Glasgow Corporation: Tuberculosis case notification rate",
    subtitle = "1950 to 1963, by TB disease classification",
    x = "Year",
    y = "Case notification rate (per 100,000)",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)"
  ) +
  theme_ggdist() +
  theme(legend.position = "bottom")

NA
NA
NA

5.2 TB case notification rates by Division


division_inc <- division_pops %>%
  left_join(cases_by_division)
Joining with `by = join_by(division, year)`
division_inc <- division_inc %>%
  mutate(inc_100k = cases/total_population*100000)

division_inc %>%
  select(year, division, tb_type, inc_100k) %>%
  mutate_at(.vars = vars(inc_100k),
            .funs = funs(round)) %>%
  datatable()
Warning: `funs()` was deprecated in dplyr 0.8.0.
Please use a list of either functions or lambdas: 

  # Simple named list: 
  list(mean = mean, median = median)

  # Auto named with `tibble::lst()`: 
  tibble::lst(mean, median)

  # Using lambdas
  list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))

division_inc %>%
  mutate(tb_type = case_when(tb_type == "Pulmonary" ~ "Pulmonary TB",
                          tb_type == "Non-Pulmonary" ~ "Extra-pulmonary TB")) %>%
  ggplot() +
  geom_area(aes(y=inc_100k, x=year2, group = tb_type, fill=tb_type), alpha=0.5) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  facet_wrap(division~.) +
  scale_fill_brewer(palette = "Set1", name="") +
  labs(
    title = "Glasgow Corporation: Tuberculosis case notification rate, by Division",
    subtitle = "1950 to 1963, by TB disease classification",
    x = "Year",
    y = "Case notification rate (per 100,000)",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)\nNote: extra-pulmonary TB cases by Division/Ward not reported in 1962-1963"
  ) +
  theme_ggdist() +
  theme(legend.position = "bottom")

NA
NA
NA

5.2 TB case notification rates by Ward

Here we will filter out the institutions and harbour from the denominators, as we don’t have reliable population denominators for them.


ward_inc <- ward_pops %>%
  left_join(cases_by_ward)
Joining with `by = join_by(ward, year, year2)`
ward_inc <- ward_inc %>%
  mutate(inc_100k = cases/population_without_inst_ship*100000)

ward_inc %>%
  select(year, ward, tb_type, inc_100k) %>%
  mutate_at(.vars = vars(inc_100k),
            .funs = funs(round)) %>%
  datatable()
Warning: `funs()` was deprecated in dplyr 0.8.0.
Please use a list of either functions or lambdas: 

  # Simple named list: 
  list(mean = mean, median = median)

  # Auto named with `tibble::lst()`: 
  tibble::lst(mean, median)

  # Using lambdas
  list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))

ward_inc %>%
  mutate(tb_type = case_when(tb_type == "Pulmonary" ~ "Pulmonary TB",
                          tb_type == "Non-Pulmonary" ~ "Extra-pulmonary TB")) %>%
  ggplot() +
  geom_area(aes(y=inc_100k, x=year2, group = tb_type, fill=tb_type), alpha=0.5) +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels,
                     guide = guide_axis(angle = 90)) +
  facet_wrap(ward~.) +
  scale_fill_brewer(palette = "Set1", name="") +
  labs(
    title = "Glasgow Corporation: Tuberculosis case notification rate, by Ward",
    subtitle = "1950 to 1963, by TB disease classification",
    x = "Year",
    y = "Incidence (per 100,000)",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)\nNote: extra-pulmonary TB cases by Division/Ward not reported in 1962-1963"
  ) +
  theme(legend.position = "bottom")

NA
NA
NA
NA

On a map


st_as_sf(left_join(ward_inc, glasgow_wards_1951)) %>%
  filter(tb_type=="Pulmonary") %>%
  ggplot() +
  geom_sf(aes(fill=inc_100k)) +
  facet_wrap(year~., ncol = 7) +
  scale_fill_viridis_c(name="Case notification rate (per 100,000)",
                       option = "A") +
  theme_ggdist() +
  theme(legend.position = "top",
        legend.key.width = unit(2, "cm"),
        panel.border = element_rect(colour = "grey78", fill=NA)) +
  guides(fill=guide_colorbar(title.position = "top"))
Joining with `by = join_by(division, ward, ward_number)`

6. TB Mortality

6.1 Overall Mortality

Import the TB mortality data.

First, overall deaths. Note that in the original reports, we have a pulmonary TB death rate per million for all years, and numbers of pulmonary TB deaths for each year apart from 1950.


#get the overall mortality sheets
deaths_sheets <- enframe(all_sheets) %>%
  filter(grepl("deaths", value)) %>%
  pull(value)


overall_deaths <- map_df(deaths_sheets, ~read_xlsx(path = "2023-11-28_glasgow-acf.xlsx",
                                sheet = .))

overall_deaths %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) %>%
  datatable()
NA
NA
NA

Plot the raw numbers of pulmonary deaths


overall_deaths %>%
  ggplot(aes(x=year, y=pulmonary_deaths)) +
  geom_line(colour = "#DE0D92") +
  geom_point(colour = "#DE0D92") +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  labs(y="Pulmonary TB deaths per year",
       x = "Year",
       title = "Numbers of pulmonary TB deaths",
       subtitle = "Glasgow, 1950-1963",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)\nNote: no data for 1950") +
  theme_ggdist() +
  theme(panel.border = element_rect(colour = "grey78", fill=NA))

NA
NA

Now the incidence of pulmonary TB death

overall_deaths %>%
  ggplot(aes(x=year, y=pulmonary_death_rate_per_100k)) +
  geom_line(colour = "#4D6CFA") +
  geom_point(colour = "#4D6CFA") +
  geom_vline(aes(xintercept=acf_start), linetype=3) +
  geom_vline(aes(xintercept=acf_end), linetype=3) +
  scale_y_continuous(labels=comma) +
  scale_x_continuous(labels = year_labels,
                     breaks = year_labels) +
  labs(y="Annual incidence of death (per 100,000)",
       x = "Year",
    caption = "Mass miniature X-ray campaign period between dashed lines (11th March-12th April 1957)") +
  theme_ggdist() +
  theme(panel.border = element_rect(colour = "grey78", fill=NA))

ggsave(here("figures/s7.png"), width=10)
Saving 10 x 4.51 in image

6. Table 1

Make Table 1 here, and save for publication.


overall_pops %>% 
  select(year, total_population) %>%
  left_join(overall_inc %>%
              select(year, 
                     pulmonary_notifications, inc_pulm_100k,
                     `non-pulmonary_notifications`, inc_ep_100k,
                     total_notifications, inc_100k)) %>%
  left_join(overall_deaths %>%
              select(year,
                     pulmonary_deaths, pulmonary_death_rate_per_100k)) %>%
  mutate(across(where(is.numeric) & !(year),  ~round(., digits=1))) %>%
  mutate(across(where(is.numeric) & !(year),  ~comma(.))) 
Joining with `by = join_by(year)`Joining with `by = join_by(year)`

Prepare the datasets for modelling


mdata <- ward_inc %>%
  filter(tb_type=="Pulmonary") %>%
  mutate(acf_period = case_when(year %in% c(1950:1956) ~ "a. pre-acf",
                                year %in% c(1957) ~ "b. acf",
                                year %in% c(1958:1963) ~ "c. post-acf")) %>%
  group_by(ward) %>%
  mutate(y_num = row_number()) %>%
  ungroup()


mdata_extrapulmonary <- ward_inc %>%
  filter(tb_type=="Non-Pulmonary") %>%
  mutate(acf_period = case_when(year %in% c(1950:1956) ~ "a. pre-acf",
                                year %in% c(1957) ~ "b. acf",
                                year %in% c(1958:1963) ~ "c. post-acf")) %>%
  group_by(ward) %>%
  mutate(y_num = row_number()) %>%
  ungroup() %>% 
  filter(year<=1961) #no data for 1962 and 1963


#scaffold for overall predictions
overall_scaffold <- mdata %>%
    select(year, year2, y_num, acf_period, population_without_inst_ship, ward, cases) %>%
    group_by(year, year2, y_num, acf_period) %>%
    summarise(population_without_inst_ship = sum(population_without_inst_ship),
              cases = sum(cases)) %>%
    ungroup() %>%
    mutate(inc_100k = cases/population_without_inst_ship*100000) %>%
    left_join(mdata_extrapulmonary %>% group_by(year) %>%
                summarise(cases_extrapulmonary = sum(cases))) %>%
    mutate(inc_100k_extrapulmonary = cases_extrapulmonary/population_without_inst_ship*100000)
`summarise()` has grouped output by 'year', 'year2', 'y_num'. You can override using the `.groups` argument.Joining with `by = join_by(year)`

7. Pulmonary TB model

7.1 Fit the model and priors

This models the case notification rate over time, with a step change for the intervention, and slope change after the intervention.

Work on the priors a bit. We will build up from less complex to more complex.

  1. intercept only, to predict count of cases

at the intercept, we expect somewhere around 2500. We will set the standard deviation to both 0.5 and 1 to check what it looks like

# 
# c(prior(lognormal(7.600902, 0.5)), #log(2500) = 7.600902
#   prior(lognormal(7.600902, 1))) %>% 
#   parse_dist() %>% 
#   
#   ggplot(aes(y = prior, dist = .dist, args = .args)) +
#   stat_halfeye(.width = c(.5, .95)) +
#   scale_y_discrete(NULL, labels = str_c("lognormal(log(2000), ", c(0.5, 1), ")"),
#                    expand = expansion(add = 0.1)) +
#   xlab(expression(exp(italic(p)(beta[0])))) +
#   coord_cartesian(xlim = c(0,15000))
# 
# 
# prior(gamma(1, 0.01)) %>%
#   parse_dist() %>%
#   ggplot(aes(y=prior, dist = .dist, args = .args)) +
#   stat_halfeye(.width = c(0.5, 0.95))
# 
# #now fit to a model, and plot some prior realisations
# 
# m_prior1 <- brm(
#   cases ~ 0 + Intercept,
#   family = negbinomial(),
#   data = overall_scaffold,
#   sample_prior = "only",
#   prior = prior(normal(log(2000), 0.5), class = b, coef = Intercept) +
#           prior(gamma(1, 0.01), class = shape)
# )
# 
# add_epred_draws(object=m_prior1,
#                 newdata = tibble(intercept=1)) %>%
#   ggplot(aes(x=intercept, y=.epred)) +
#   stat_halfeye() +
#   scale_y_log10(labels = comma)

Now try to add in a term for the effect of y_num. We anticpate that the number of cases will decline by about 1-5% per year. However, as we are pretty uncertain about this, we will just encode a weakly regularising prior to restrict the year size to sensible ranges.

# 
# 
# m_prior2 <- brm(
#   cases ~ 0 + Intercept + y_num,
#   family = negbinomial(),
#   data = overall_scaffold,
#   sample_prior = "only",
#   prior = prior(normal(log(2000), 0.5), class = b, coef = Intercept) +
#           prior(gamma(1, 0.01), class = shape) +
#           prior(normal(0, 0.01), class = b, coef = y_num)
# )
# 
# add_epred_draws(object=m_prior2,
#                 newdata = overall_scaffold) %>%
#   ggplot(aes(x=year, y=.epred)) +
#   stat_halfeye() +
#   scale_y_log10(label=comma)

Now we want to add in a prior for the effect of the acf_intervention. We anticipate the peak to be anywhere between no effect, and a tripling

# 
# m_prior3 <- brm(
#   cases ~ 0 + Intercept + y_num + acf_period,
#   family = negbinomial(),
#   data = overall_scaffold,
#   sample_prior = "only",
#   prior = prior(normal(log(2000), 0.5), class = b, coef = Intercept) +
#           prior(gamma(1, 0.01), class = shape) +
#           prior(normal(0, 0.01), class = b, coef = y_num) +
#           prior(normal(0, 0.001), class = b)
# )
# 
# 
# add_epred_draws(object=m_prior3,
#                 newdata = overall_scaffold) %>%
#   ggplot(aes(x=year, y=.epred)) +
#   stat_halfeye() +
#   scale_y_log10(labels = comma)
# 

Now we look and see what it looks like with the interactions

# 
# m_prior4 <- brm(
#   cases ~ 0 + Intercept + y_num + acf_period + y_num:acf_period,
#   family = negbinomial(),
#   data = overall_scaffold,
#   sample_prior = "only",
#   prior = prior(normal(log(2500), 1), class = b, coef = Intercept) +
#           prior(gamma(1, 0.01), class = shape) +
#           prior(normal(0, 0.01), class = b)
# )
# 
# add_epred_draws(object=m_prior4,
#                 newdata = overall_scaffold) %>%
#   ggplot(aes(x=year, y=.epred)) +
#   stat_halfeye() +
#   scale_y_log10(label=comma)
# 
# 

Now try adding in the random intercepts


# c(prior(lognormal(3.912023, 0.5)), #log(50) = 3.912023
#   prior(lognormal(3.912023, 1))) %>% 
#   parse_dist() %>% 
#   
#   ggplot(aes(y = prior, dist = .dist, args = .args)) +
#   stat_halfeye(.width = c(.5, .95)) +
#   scale_y_discrete(NULL, labels = str_c("lognormal(log(50), ", c(0.5, 1), ")"),
#                    expand = expansion(add = 0.1)) +
#   xlab(expression(exp(italic(p)(beta[0])))) +
#   coord_cartesian(xlim = c(0,400))
# 
# 
# m_prior5 <- brm(
#   cases ~ y_num + acf_period + y_num:acf_period + ( 1 | ward),
#   family = negbinomial(),
#   data = mdata,
#   sample_prior = "only",
#   prior = prior(normal(log(50), 1), class = Intercept) +
#           prior(gamma(1, 0.01), class = shape) +
#           prior(normal(0, 0.01), class = b) +
#           prior(exponential(1), class=sd)
# )
# 
# 
# add_epred_draws(object=m_prior5,
#                 newdata = mdata,
#                 re_formula = NA) %>%
#   ggplot(aes(x=year, y=.epred)) +
#   stat_halfeye() +
#   scale_y_log10(label=comma)
# 
# add_epred_draws(object=m_prior5,
#                 newdata = mdata,
#                 re_formula = NA) %>%
#   ggplot(aes(x=year, y=.epred)) +
#   stat_halfeye() +
#   scale_y_log10(label=comma) +
#   facet_wrap(ward~.)

And add in the random slopes

# 
# m_prior6 <- brm(
#   cases ~ 1 + y_num + acf_period + y_num:acf_period + (1 + y_num*acf_period | ward),
#   family = negbinomial(),
#   data = mdata,
#   sample_prior = "only",
#   prior = prior(gamma(1, 0.01), class = shape) +
#           prior(normal(0, 0.1), class = b) +
#           prior(exponential(1), class=sd) +
#           prior(lkj(2), class=cor)
# )
# 
# 
# 
# m_prior6 <- brm(
#   cases ~ 0 + Intercept + y_num + acf_period + y_num:acf_period + ( y_num*acf_period | ward),
#   family = negbinomial(),
#   data = mdata,
#   sample_prior = "only",
#   prior = prior(normal(log(50), 1), class = b, coef = Intercept) +
#           prior(gamma(1, 0.01), class = shape) +
#           prior(normal(0, 0.01), class = b) +
#           prior(exponential(100), class=sd) +
#           prior(lkj(2), class=cor)
# )


# add_epred_draws(object=m_prior6,
#                 newdata = mdata,
#                 re_formula = NA) %>%
#   ggplot(aes(x=year, y=.epred)) +
#   stat_halfeye() +
#   scale_y_log10(label=comma)
# 
# add_epred_draws(object=m_prior6,
#                 newdata = mdata,
#                 re_formula = ~( 1 + y_num + acf_period | ward)) %>%
#   ggplot(aes(x=year, y=.epred)) +
#   stat_halfeye() +
#   scale_y_log10(label=comma) +
#   facet_wrap(ward~.)
# 
# plot_counterfactual(model_data = overall_scaffold, model=m_prior6, outcome = inc_100k, 
#                     population_denominator = population_without_inst_ship, re_formula = NA)
# 
# plot_counterfactual(model_data = mdata, model=m_prior6, outcome = inc_100k, 
#                     population_denominator = population_without_inst_ship, grouping_var = ward, ward,
#                     re_formula = ~( 1 + y_num + acf_period | ward))

Issue here is the non-centred parameterisation of the intercept prior… Feel like this is a more interpretable way to set priors… but will revert to centred parameterisation for the meantime.

# m_centered_prior <- brm(
#   cases ~ 1 + y_num*acf_period + (1 + y_num*acf_period | ward) + offset(log(population_without_inst_ship)),
#                   data = mdata,
#                   family = negbinomial(),
#                   seed = 1234,
#                   chains = 4, cores = 4,
#                   prior = prior(normal(0,1000), class = Intercept) +
#                           prior(gamma(0.01, 0.01), class = shape) +
#                           prior(normal(0, 1), class = b) +
#                           prior(exponential(1), class=sd) +
#                           prior(lkj(2), class=cor),
#                   sample_prior = "only")
# 
# plot(m_centered_prior)
# 
# plot_counterfactual(model_data = overall_scaffold, model=m_centered_prior, outcome = inc_100k, 
#                     population_denominator = population_without_inst_ship, re_formula = NA)
# 
# plot_counterfactual(model_data = mdata, model=m_centered_prior, outcome = inc_100k, 
#                     population_denominator = population_without_inst_ship, grouping_var = ward, ward,
#                     re_formula = ~( 1 + y_num*acf_period | ward))

Look at the mean and variance of counts (counts of pulmonary notifications are what we are predicting)


#Mean of counts per year
mean(mdata$cases)
[1] 48.32819
#variance of counts per year
var(mdata$cases)
[1] 915.5749

Quite a bit of over-dispersion here, so negative binomial distribution might be a better choice of distributional family than Poisson.

Fit the model with the data


m_pulmonary <- brm(
  cases ~ 1 + y_num*acf_period + (1 + y_num*acf_period | ward) + offset(log(population_without_inst_ship)),
                  data = mdata,
                  family = negbinomial(),
                  seed = 1234,
                  chains = 4, cores = 4,
                  prior = prior(normal(0,1), class = Intercept) +
                          prior(gamma(0.01, 0.01), class = shape) +
                          prior(normal(0, 1), class = b) +
                          prior(exponential(1), class=sd) +
                          prior(lkj(4), class=cor),
  control = list(adapt_delta = 0.9))
Compiling Stan program...
Start sampling
starting worker pid=10072 on localhost:11273 at 19:31:46.486
starting worker pid=10085 on localhost:11273 at 19:31:46.579
starting worker pid=10098 on localhost:11273 at 19:31:46.669
starting worker pid=10111 on localhost:11273 at 19:31:46.760

SAMPLING FOR MODEL 'anon_model' NOW (CHAIN 1).
Chain 1: 
Chain 1: Gradient evaluation took 0.000147 seconds
Chain 1: 1000 transitions using 10 leapfrog steps per transition would take 1.47 seconds.
Chain 1: Adjust your expectations accordingly!
Chain 1: 
Chain 1: 
Chain 1: Iteration:    1 / 2000 [  0%]  (Warmup)

SAMPLING FOR MODEL 'anon_model' NOW (CHAIN 2).
Chain 2: 
Chain 2: Gradient evaluation took 0.000173 seconds
Chain 2: 1000 transitions using 10 leapfrog steps per transition would take 1.73 seconds.
Chain 2: Adjust your expectations accordingly!
Chain 2: 
Chain 2: 
Chain 2: Iteration:    1 / 2000 [  0%]  (Warmup)

SAMPLING FOR MODEL 'anon_model' NOW (CHAIN 3).
Chain 3: 
Chain 3: Gradient evaluation took 0.000149 seconds
Chain 3: 1000 transitions using 10 leapfrog steps per transition would take 1.49 seconds.
Chain 3: Adjust your expectations accordingly!
Chain 3: 
Chain 3: 
Chain 3: Iteration:    1 / 2000 [  0%]  (Warmup)

SAMPLING FOR MODEL 'anon_model' NOW (CHAIN 4).
Chain 4: 
Chain 4: Gradient evaluation took 0.000169 seconds
Chain 4: 1000 transitions using 10 leapfrog steps per transition would take 1.69 seconds.
Chain 4: Adjust your expectations accordingly!
Chain 4: 
Chain 4: 
Chain 4: Iteration:    1 / 2000 [  0%]  (Warmup)
Chain 1: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 3: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 4: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 2: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 4: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 1: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 2: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 3: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 4: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 3: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 2: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 1: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 4: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 3: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 2: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 1: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 4: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 4: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 3: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 3: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 2: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 2: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 1: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 1: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 4: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 3: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 2: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 1: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 4: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 3: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 2: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 1: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 4: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 3: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 2: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 1: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 4: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 3: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 2: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 1: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 4: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 4: 
Chain 4:  Elapsed Time: 22.953 seconds (Warm-up)
Chain 4:                18.619 seconds (Sampling)
Chain 4:                41.572 seconds (Total)
Chain 4: 
Chain 3: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 3: 
Chain 3:  Elapsed Time: 24.347 seconds (Warm-up)
Chain 3:                18.372 seconds (Sampling)
Chain 3:                42.719 seconds (Total)
Chain 3: 
Chain 2: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 2: 
Chain 2:  Elapsed Time: 24.652 seconds (Warm-up)
Chain 2:                18.47 seconds (Sampling)
Chain 2:                43.122 seconds (Total)
Chain 2: 
Chain 1: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 1: 
Chain 1:  Elapsed Time: 25.503 seconds (Warm-up)
Chain 1:                18.491 seconds (Sampling)
Chain 1:                43.994 seconds (Total)
Chain 1: 
  
#check model diagnostics
summary(m_pulmonary)
 Family: negbinomial 
  Links: mu = log; shape = identity 
Formula: cases ~ 1 + y_num * acf_period + (1 + y_num * acf_period | ward) + offset(log(population_without_inst_ship)) 
   Data: mdata (Number of observations: 518) 
  Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
         total post-warmup draws = 4000

Group-Level Effects: 
~ward (Number of levels: 37) 
                                                      Estimate Est.Error l-95% CI u-95% CI
sd(Intercept)                                             0.25      0.03     0.19     0.32
sd(y_num)                                                 0.02      0.01     0.01     0.03
sd(acf_periodb.acf)                                       0.06      0.04     0.00     0.16
sd(acf_periodc.postMacf)                                  0.11      0.06     0.01     0.24
sd(y_num:acf_periodb.acf)                                 0.01      0.01     0.00     0.02
sd(y_num:acf_periodc.postMacf)                            0.01      0.01     0.00     0.02
cor(Intercept,y_num)                                     -0.46      0.20    -0.76    -0.00
cor(Intercept,acf_periodb.acf)                           -0.23      0.28    -0.71     0.36
cor(y_num,acf_periodb.acf)                               -0.04      0.27    -0.54     0.48
cor(Intercept,acf_periodc.postMacf)                      -0.14      0.24    -0.59     0.35
cor(y_num,acf_periodc.postMacf)                           0.10      0.25    -0.40     0.57
cor(acf_periodb.acf,acf_periodc.postMacf)                 0.06      0.28    -0.49     0.57
cor(Intercept,y_num:acf_periodb.acf)                     -0.25      0.29    -0.72     0.37
cor(y_num,y_num:acf_periodb.acf)                         -0.04      0.27    -0.55     0.49
cor(acf_periodb.acf,y_num:acf_periodb.acf)               -0.06      0.28    -0.59     0.49
cor(acf_periodc.postMacf,y_num:acf_periodb.acf)           0.07      0.27    -0.48     0.57
cor(Intercept,y_num:acf_periodc.postMacf)                -0.02      0.26    -0.50     0.49
cor(y_num,y_num:acf_periodc.postMacf)                    -0.03      0.27    -0.54     0.51
cor(acf_periodb.acf,y_num:acf_periodc.postMacf)           0.05      0.28    -0.50     0.57
cor(acf_periodc.postMacf,y_num:acf_periodc.postMacf)     -0.08      0.29    -0.63     0.47
cor(y_num:acf_periodb.acf,y_num:acf_periodc.postMacf)     0.05      0.27    -0.49     0.55
                                                      Rhat Bulk_ESS Tail_ESS
sd(Intercept)                                         1.00     1465     2204
sd(y_num)                                             1.00     1072     1047
sd(acf_periodb.acf)                                   1.00     2045     2257
sd(acf_periodc.postMacf)                              1.00      913     1346
sd(y_num:acf_periodb.acf)                             1.01     1233     1598
sd(y_num:acf_periodc.postMacf)                        1.01      709     1430
cor(Intercept,y_num)                                  1.00     2716     2233
cor(Intercept,acf_periodb.acf)                        1.00     3541     2755
cor(y_num,acf_periodb.acf)                            1.00     6229     3130
cor(Intercept,acf_periodc.postMacf)                   1.00     5511     2516
cor(y_num,acf_periodc.postMacf)                       1.00     2979     3082
cor(acf_periodb.acf,acf_periodc.postMacf)             1.00     2417     2633
cor(Intercept,y_num:acf_periodb.acf)                  1.00     3650     3281
cor(y_num,y_num:acf_periodb.acf)                      1.00     6105     3309
cor(acf_periodb.acf,y_num:acf_periodb.acf)            1.00     5303     3005
cor(acf_periodc.postMacf,y_num:acf_periodb.acf)       1.00     4234     3467
cor(Intercept,y_num:acf_periodc.postMacf)             1.00     5928     2788
cor(y_num,y_num:acf_periodc.postMacf)                 1.00     4067     3118
cor(acf_periodb.acf,y_num:acf_periodc.postMacf)       1.00     2699     3112
cor(acf_periodc.postMacf,y_num:acf_periodc.postMacf)  1.00     3352     3170
cor(y_num:acf_periodb.acf,y_num:acf_periodc.postMacf) 1.00     2574     3352

Population-Level Effects: 
                           Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept                     -6.14      0.05    -6.24    -6.04 1.00     1003     1885
y_num                         -0.02      0.01    -0.03    -0.01 1.00     3625     3364
acf_periodb.acf                0.03      1.01    -1.99     2.02 1.00     3980     2998
acf_periodc.postMacf           0.05      0.10    -0.15     0.25 1.00     4687     3536
y_num:acf_periodb.acf          0.08      0.13    -0.17     0.33 1.00     3970     3191
y_num:acf_periodc.postMacf    -0.05      0.01    -0.07    -0.03 1.00     4692     3159

Family Specific Parameters: 
      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
shape    91.79     20.34    60.88   140.54 1.00     2971     2721

Draws were sampled using sampling(NUTS). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
plot(m_pulmonary)


pp_check(m_pulmonary, type='ecdf_overlay')
Using 10 posterior draws for ppc type 'ecdf_overlay' by default.

7.2 Summarise change in CNRs

Summarise the posterior in graphical form

prior_summary(m_pulmonary)
                prior     class                       coef group resp dpar nlpar lb ub
         normal(0, 1)         b                                                       
         normal(0, 1)         b            acf_periodb.acf                            
         normal(0, 1)         b       acf_periodc.postMacf                            
         normal(0, 1)         b                      y_num                            
         normal(0, 1)         b      y_num:acf_periodb.acf                            
         normal(0, 1)         b y_num:acf_periodc.postMacf                            
         normal(0, 1) Intercept                                                       
 lkj_corr_cholesky(4)         L                                                       
 lkj_corr_cholesky(4)         L                             ward                      
       exponential(1)        sd                                                   0   
       exponential(1)        sd                             ward                  0   
       exponential(1)        sd            acf_periodb.acf  ward                  0   
       exponential(1)        sd       acf_periodc.postMacf  ward                  0   
       exponential(1)        sd                  Intercept  ward                  0   
       exponential(1)        sd                      y_num  ward                  0   
       exponential(1)        sd      y_num:acf_periodb.acf  ward                  0   
       exponential(1)        sd y_num:acf_periodc.postMacf  ward                  0   
    gamma(0.01, 0.01)     shape                                                   0   
       source
         user
 (vectorized)
 (vectorized)
 (vectorized)
 (vectorized)
 (vectorized)
         user
         user
 (vectorized)
         user
 (vectorized)
 (vectorized)
 (vectorized)
 (vectorized)
 (vectorized)
 (vectorized)
 (vectorized)
         user
f1b <- plot_counterfactual(model_data = overall_scaffold, model = m_pulmonary, 
                           population_denominator = population_without_inst_ship, outcome = inc_100k, grouping_var=NULL,
                           re_formula = NA)
  
f1b

Make this into a figure combined with the map of empirical data


f1a <- st_as_sf(left_join(ward_inc, glasgow_wards_1951)) %>%
  filter(tb_type=="Pulmonary") %>%
  ggplot() +
  geom_sf(aes(fill=inc_100k)) +
  facet_wrap(year~., ncol = 7) +
  scale_fill_viridis_c(name="Case notification rate (per 100,000)",
                       option = "A") +
  theme_ggdist() +
  theme(panel.border = element_rect(colour = "grey78", fill=NA),
        legend.position = "top",
        legend.key.width = unit(2, "cm"),
        legend.title.align = 0.5,
        text = element_text(size=8),
        axis.text.x = element_text(angle=45, size=6, hjust=1),
        axis.text.y = element_text(size=6)) +
  guides(fill=guide_colorbar(title.position = "top"))
Joining with `by = join_by(division, ward, ward_number)`
(f1a / f1b) + plot_annotation(tag_levels = "A")

ggsave(here("figures/f1.png"))
Saving 7.29 x 4.51 in image

Summary of change in notifications numerically


overall_change <- summarise_change(model_data=overall_scaffold, model=m_pulmonary, 
                                   population_denominator=population_without_inst_ship, grouping_var=NULL, re_formula = NA)
`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.
#want to keep the summary estimates here
tokeep <- c("peak_summary", "level_summary", "slope_summary")

#summary measures in a table
overall_change %>%
  keep((names(.) %in% tokeep)) %>%
  bind_rows() %>%
  mutate(across(c(estimate:.upper), number, accuracy=0.01)) %>%
  select(measure, everything()) %>%
  datatable()
Warning: There was 1 warning in `mutate()`.
ℹ In argument: `across(c(estimate:.upper), number, accuracy = 0.01)`.
Caused by warning:
! The `...` argument of `across()` is deprecated as of dplyr 1.1.0.
Supply arguments directly to `.fns` through an anonymous function instead.

  # Previously
  across(a:b, mean, na.rm = TRUE)

  # Now
  across(a:b, \(x) mean(x, na.rm = TRUE))
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
NA

7.3 Compared to counterfactual

Numbers of pulmonary TB cases averted compared to counterfactual per year.


overall_pulmonary_counterf <- calculate_counterfactual(model_data = overall_scaffold, model=m_pulmonary, population_denominator = population_without_inst_ship)
Joining with `by = join_by(year, population_without_inst_ship, .draw)`Joining with `by = join_by(.draw)`
overall_pulmonary_counterf$counter_post %>%
  mutate(across(c(cases_averted:cases_averted.upper, diff_inc100k:diff_inc100k.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(rr_inc100k:rr_inc100k.upper), number_format(accuracy = 0.01))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  datatable()
NA
NA

Total pulmonary TB cases averted between 1958 and 1963


overall_pulmonary_counterf$counter_post_overall %>%
  mutate(across(c(cases_averted:cases_averted.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  datatable()
NA
NA

7.4 Correlation between RR.peak, RR.level, and RR.slope

What are the correlations between peak, level, and slope?


#RR.peak histogram
a <- overall_change$peak_draws %>%
  ggplot() +
  geom_histogram(aes(x=estimate), fill="darkblue", colour="darkblue", alpha=0.3)+
  scale_fill_gradient(high="lightblue1",low="darkblue") +
  theme_ggdist() +
  theme(legend.position = "none",
        panel.border = element_rect(colour = "grey78", fill=NA)) +
  labs(x="RR.peak",
       y="")

#RR. level histogram
b <- overall_change$level_draws  %>%
  ggplot() +
  geom_histogram(aes(x=estimate), fill="darkblue", colour="darkblue", alpha=0.3)+
  scale_fill_gradient(high="lightblue1",low="darkblue") +
  theme_ggdist() +
  theme(legend.position = "none",
        panel.border = element_rect(colour = "grey78", fill=NA)) +
  labs(x="RR.level",
       y="")

#RR.slope histogram
c <- overall_change$slope_draws %>%
  ggplot() +
  geom_histogram(aes(x=estimate), fill="darkblue", colour="darkblue", alpha=0.3)+
  scale_fill_gradient(high="lightblue1",low="darkblue") +  
  #scale_x_continuous(limits = c(0, 6)) +
  theme_ggdist() +
  theme(legend.position = "none",
        panel.border = element_rect(colour = "grey78", fill=NA)) +
  labs(x="RR.slope (logit scale)",
       y="")


#Correlation between RR.peak and RR.level
cor_rr_peak_rr_level <- round(cor(pluck(overall_change$peak_draws$estimate), pluck(overall_change$level_draws$estimate)), digits = 2)

#Correlation between RR.peak and RR.slope
cor_rr_peak_rr_slope <- round(cor(pluck(overall_change$peak_draws$estimate), pluck(overall_change$slope_draws$estimate)), digits = 2)

#Correlation between RR.level and RR.slope
cor_rr_level_rr_slope <- round(cor(pluck(overall_change$level_draws$estimate), pluck(overall_change$slope_draws$estimate)), digits = 2)


#plot of correlation between RR.peak and RR.level
d <- bind_cols(RR.peak=pluck(overall_change$peak_draws$estimate), 
          RR.level =pluck(overall_change$level_draws$estimate)) %>%
  ggplot(aes(y=RR.peak, x = RR.level)) +
  geom_hex() +
  geom_smooth(se=FALSE, colour="firebrick", method = "lm") +
  geom_text(aes(y=2.2, x=0.58, label=cor_rr_peak_rr_level), colour="firebrick")  +
  scale_fill_gradient(high="lightblue1",low="darkblue") +
  theme_ggdist() +
  theme(legend.position = "none",
        panel.border = element_rect(colour = "grey78", fill=NA))

#plot of correlation between RR.peak and RR.slope
e <- bind_cols(RR.peak=pluck(overall_change$peak_draws$estimate), 
          RR.slope =pluck(overall_change$slope_draws$estimate)) %>%
  ggplot(aes(y=RR.peak, x = RR.slope)) +
  geom_hex() +
  geom_smooth(se=FALSE, colour="firebrick", method = "lm") +
  geom_text(aes(y=2.1, x=0.5, label=cor_rr_peak_rr_slope), colour="firebrick")  +
  #scale_x_continuous(limits = c(0, 6)) +
  scale_fill_gradient(high="lightblue1",low="darkblue") +
  theme_ggdist() +
  theme(legend.position = "none",
        panel.border = element_rect(colour = "grey78", fill=NA))

#plot of correlation between RR.level and RR.slope
f <- bind_cols(RR.level=pluck(overall_change$level_draws$estimate), 
          RR.slope =pluck(overall_change$slope_draws$estimate)) %>%
  ggplot(aes(y=RR.level, x = RR.slope)) +
  geom_hex() +
  geom_smooth(se=FALSE, colour="firebrick", method = "lm") +
  geom_text(aes(y=0.75, x=0.5, label=cor_rr_level_rr_slope), colour="firebrick")  +  
  #scale_x_continuous(limits = c(0, 6)) +
  scale_fill_gradient(high="lightblue1",low="darkblue") +
  theme_ggdist() +
  theme(legend.position = "none",
        panel.border = element_rect(colour = "grey78", fill=NA))


(plot_spacer() + plot_spacer() + c) /
  (plot_spacer() + b + f) /
  (a + d + e)
ℹ The package "hexbin" is required for `stat_binhex()`
✖ Would you like to install it?

1: Yes
2: No
1
trying URL 'https://cran.rstudio.com/bin/macosx/big-sur-arm64/contrib/4.3/hexbin_1.28.3.tgz'
Content type 'application/x-gzip' length 1609993 bytes (1.5 MB)
==================================================
downloaded 1.5 MB

The downloaded binary packages are in
    /var/folders/n2/yxj0lwh51wq545d2xfnhh7740000gn/T//RtmpGldgYw/downloaded_packages
ggsave(here("figures/pulmonary_cors.pdf"), width=8, height=8)

NA
NA
NA

7.5 Ward level pulmonary TB estimates

Plot the counterfactual at ward level


plot_counterfactual(model_data = mdata, model=m_pulmonary, outcome = inc_100k, population_denominator = population_without_inst_ship, 
                    grouping_var = ward, ward, re_formula= ~(1 + y_num*acf_period | ward))
  
ggsave(here("figures/s3.png"), width=12, height=12)

Summary of change in notifications at ward level


ward_change <- summarise_change(model_data=mdata, model=m_pulmonary, 
                                   population_denominator=population_without_inst_ship, grouping_var=ward, 
                                   re_formula = ~(1 + y_num*acf_period | ward))
`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.`summarise()` has grouped output by '.draw', 'acf_period'. You can override using the `.groups` argument.`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.
#want to keep the summary estimates here
tokeep <- c("peak_summary", "level_summary", "slope_summary")

#summary measures in a table
ward_change %>%
  keep((names(.) %in% tokeep)) %>%
  bind_rows() %>%
  mutate(across(c(estimate:.upper), number, accuracy=0.01)) %>%
  select(measure, everything()) %>%
  datatable()


#plot these in a figure
ward_effects <- ward_change %>%
  keep((names(.) %in% tokeep)) %>%
  bind_rows() %>%
  bind_rows(overall_change$peak_summary) %>%
  bind_rows(overall_change$level_summary) %>%
  bind_rows(overall_change$slope_summary) %>%
  mutate_at(.vars = vars(estimate:.upper), 
            .funs = funs(as.numeric)) %>%
  select(measure, everything()) %>%
  mutate(estimate = as.double(estimate)) %>%
  full_join(glasgow_wards_1951) %>% 
  mutate(ward2 = paste0(ward_number, ". ", ward)) %>%
  mutate(ward2 = case_when(is.na(ward) ~ "Overall",
                          TRUE ~ ward2)) %>%
  st_as_sf() 
Warning: `funs()` was deprecated in dplyr 0.8.0.
Please use a list of either functions or lambdas: 

  # Simple named list: 
  list(mean = mean, median = median)

  # Auto named with `tibble::lst()`: 
  tibble::lst(mean, median)

  # Using lambdas
  list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))Joining with `by = join_by(ward)`
#function for plotting choropleth maps
plot_ward_effect <- function(data, measure){
  {{data}} %>%
  filter(measure == {{measure}}) %>%
  ggplot() +
  geom_sf(aes(fill=estimate)) +
  geom_sf_label(aes(label = ward_number), size=3, fill=NA, label.size = NA, colour="black") +
  scale_fill_gradient(high="lightblue1",low="darkblue", name="") +
  theme_ggdist() +
  theme(panel.border = element_rect(colour = "grey78", fill=NA),
        axis.text.x=element_text(angle=45, hjust=1)) +
    labs(x="", y="")
}

#function for plotting catapiller plots
plot_ward_cat <- function(data, measure, scale){

  pal <- colorRampPalette(c('darkblue','lightblue'))

  {{data}} %>%
    filter(measure=={{measure}}) %>%
    mutate(my_palette = case_when(ward2=="Overall" ~ "#C60C30",
                                TRUE ~ pal(36)[as.numeric(cut(.$estimate,breaks = 36))])) %>%
    ggplot() +
    geom_pointrange(aes(y=estimate, ymin=.lower, ymax=.upper, 
                      x=fct_reorder(ward2, estimate), colour=my_palette)) +
    coord_flip() +
    scale_colour_identity(name="") +
    scale_y_continuous() +
    theme_ggdist() +
    theme(panel.border = element_rect(colour = "grey78", fill=NA)) +
    labs(x = "",
         y = "Relative rate (95% CrI)")
}



ward_peak_i <- plot_ward_effect(data = ward_effects, measure = "RR.peak")
ward_level_i <- plot_ward_effect(data = ward_effects, measure = "RR.level")
ward_slope_i <- plot_ward_effect(data = ward_effects, measure = "RR.slope")

ward_peak_ii <- plot_ward_cat(data = ward_effects, measure = "RR.peak")
ward_level_ii <- plot_ward_cat(data = ward_effects, measure = "RR.level")
ward_slope_ii <- plot_ward_cat(data = ward_effects, measure = "RR.slope")

s4 <- (ward_peak_i + ward_level_i + ward_slope_i) /
  (ward_peak_ii + ward_level_ii + ward_slope_ii)

s4[[1]] <- s4[[1]] + plot_layout(tag_level = 'new')
s4[[2]] <- s4[[2]] + plot_layout(tag_level = 'new')
s4 + plot_annotation(tag_levels = c('A', '1'))


ggsave(here("figures/s4.png"), width = 16, height=10)

Calculate the counterfactual per ward


ward_pulmonary_counterf <- calculate_counterfactual(model_data = mdata, model=m_pulmonary, 
                                                    population_denominator = population_without_inst_ship,
                                                    grouping_var = ward, re_formula=~(1 + y_num*acf_period | ward))
`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.Joining with `by = join_by(year, population_without_inst_ship, .draw, ward)`Joining with `by = join_by(.draw, ward)`
ward_pulmonary_counterf$counter_post %>%
  mutate(across(c(cases_averted:cases_averted.upper, diff_inc100k:diff_inc100k.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(rr_inc100k:rr_inc100k.upper), number_format(accuracy = 0.01))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  datatable()
NA
NA

Overall counterfactual per ward


ward_pulmonary_counterf$counter_post_overall %>%
  mutate(across(c(cases_averted:cases_averted.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  datatable()
NA

8. Extra-pulmonary TB notifications

Now we will model the extra-pulmonary TB notification rate. Struggling a bit with negative binomial model, so revert to Poisson.

8.1 Fit the model


m_extrapulmonary <- brm(
  cases ~ 1 + y_num*acf_period + (1 + y_num*acf_period | ward) + offset(log(population_without_inst_ship)),
                  data = mdata_extrapulmonary,
                  family = negbinomial(),
                  seed = 1234,
                  chains = 4, cores = 4,
                  prior = prior(normal(0,1000), class = Intercept) +
                          prior(gamma(0.01, 0.01), class = shape) +
                          prior(normal(0, 1), class = b) +
                          prior(exponential(1), class=sd) +
                          prior(lkj(2), class=cor))
Compiling Stan program...
Start sampling
starting worker pid=10277 on localhost:11273 at 19:34:23.655
starting worker pid=10292 on localhost:11273 at 19:34:23.744
starting worker pid=10306 on localhost:11273 at 19:34:23.840
starting worker pid=10319 on localhost:11273 at 19:34:23.932

SAMPLING FOR MODEL 'anon_model' NOW (CHAIN 1).
Chain 1: 
Chain 1: Gradient evaluation took 0.000345 seconds
Chain 1: 1000 transitions using 10 leapfrog steps per transition would take 3.45 seconds.
Chain 1: Adjust your expectations accordingly!
Chain 1: 
Chain 1: 
Chain 1: Iteration:    1 / 2000 [  0%]  (Warmup)

SAMPLING FOR MODEL 'anon_model' NOW (CHAIN 2).
Chain 2: 
Chain 2: Gradient evaluation took 0.000157 seconds
Chain 2: 1000 transitions using 10 leapfrog steps per transition would take 1.57 seconds.
Chain 2: Adjust your expectations accordingly!
Chain 2: 
Chain 2: 
Chain 2: Iteration:    1 / 2000 [  0%]  (Warmup)

SAMPLING FOR MODEL 'anon_model' NOW (CHAIN 3).
Chain 3: 
Chain 3: Gradient evaluation took 0.000151 seconds
Chain 3: 1000 transitions using 10 leapfrog steps per transition would take 1.51 seconds.
Chain 3: Adjust your expectations accordingly!
Chain 3: 
Chain 3: 
Chain 3: Iteration:    1 / 2000 [  0%]  (Warmup)

SAMPLING FOR MODEL 'anon_model' NOW (CHAIN 4).
Chain 4: 
Chain 4: Gradient evaluation took 0.000159 seconds
Chain 4: 1000 transitions using 10 leapfrog steps per transition would take 1.59 seconds.
Chain 4: Adjust your expectations accordingly!
Chain 4: 
Chain 4: 
Chain 4: Iteration:    1 / 2000 [  0%]  (Warmup)
Chain 2: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 1: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 3: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 4: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 2: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 1: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 3: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 4: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 2: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 1: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 2: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 3: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 4: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 2: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 2: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 1: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 3: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 4: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 2: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 3: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 3: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 1: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 1: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 4: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 4: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 2: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 3: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 1: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 4: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 2: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 3: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 2: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 3: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 1: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 4: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 2: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 2: 
Chain 2:  Elapsed Time: 8.244 seconds (Warm-up)
Chain 2:                4.206 seconds (Sampling)
Chain 2:                12.45 seconds (Total)
Chain 2: 
Chain 3: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 1: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 4: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 3: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 3: 
Chain 3:  Elapsed Time: 9.136 seconds (Warm-up)
Chain 3:                4.067 seconds (Sampling)
Chain 3:                13.203 seconds (Total)
Chain 3: 
Chain 4: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 1: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 4: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 4: 
Chain 4:  Elapsed Time: 9.422 seconds (Warm-up)
Chain 4:                5.736 seconds (Sampling)
Chain 4:                15.158 seconds (Total)
Chain 4: 
Chain 1: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 1: 
Chain 1:  Elapsed Time: 9.347 seconds (Warm-up)
Chain 1:                6.205 seconds (Sampling)
Chain 1:                15.552 seconds (Total)
Chain 1: 
summary(m_extrapulmonary)
 Family: negbinomial 
  Links: mu = log; shape = identity 
Formula: cases ~ 1 + y_num * acf_period + (1 + y_num * acf_period | ward) + offset(log(population_without_inst_ship)) 
   Data: mdata_extrapulmonary (Number of observations: 444) 
  Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
         total post-warmup draws = 4000

Group-Level Effects: 
~ward (Number of levels: 37) 
                                                      Estimate Est.Error l-95% CI u-95% CI
sd(Intercept)                                             0.33      0.06     0.23     0.45
sd(y_num)                                                 0.02      0.01     0.00     0.05
sd(acf_periodb.acf)                                       0.11      0.09     0.00     0.33
sd(acf_periodc.postMacf)                                  0.13      0.10     0.00     0.36
sd(y_num:acf_periodb.acf)                                 0.01      0.01     0.00     0.04
sd(y_num:acf_periodc.postMacf)                            0.02      0.01     0.00     0.04
cor(Intercept,y_num)                                     -0.11      0.31    -0.65     0.52
cor(Intercept,acf_periodb.acf)                           -0.01      0.33    -0.64     0.59
cor(y_num,acf_periodb.acf)                               -0.02      0.33    -0.63     0.62
cor(Intercept,acf_periodc.postMacf)                      -0.07      0.33    -0.66     0.59
cor(y_num,acf_periodc.postMacf)                          -0.06      0.33    -0.66     0.58
cor(acf_periodb.acf,acf_periodc.postMacf)                 0.01      0.33    -0.62     0.62
cor(Intercept,y_num:acf_periodb.acf)                     -0.01      0.33    -0.64     0.62
cor(y_num,y_num:acf_periodb.acf)                         -0.01      0.33    -0.63     0.60
cor(acf_periodb.acf,y_num:acf_periodb.acf)               -0.08      0.34    -0.71     0.58
cor(acf_periodc.postMacf,y_num:acf_periodb.acf)           0.01      0.32    -0.62     0.63
cor(Intercept,y_num:acf_periodc.postMacf)                -0.15      0.32    -0.69     0.51
cor(y_num,y_num:acf_periodc.postMacf)                    -0.06      0.33    -0.66     0.59
cor(acf_periodb.acf,y_num:acf_periodc.postMacf)           0.03      0.33    -0.62     0.65
cor(acf_periodc.postMacf,y_num:acf_periodc.postMacf)     -0.10      0.35    -0.73     0.58
cor(y_num:acf_periodb.acf,y_num:acf_periodc.postMacf)     0.02      0.33    -0.62     0.64
                                                      Rhat Bulk_ESS Tail_ESS
sd(Intercept)                                         1.00     1525     2401
sd(y_num)                                             1.01      503      758
sd(acf_periodb.acf)                                   1.00     1672     1222
sd(acf_periodc.postMacf)                              1.00     1456     1707
sd(y_num:acf_periodb.acf)                             1.00     1905     1936
sd(y_num:acf_periodc.postMacf)                        1.00      913     1038
cor(Intercept,y_num)                                  1.00     2043     2575
cor(Intercept,acf_periodb.acf)                        1.00     4028     2840
cor(y_num,acf_periodb.acf)                            1.00     4054     3108
cor(Intercept,acf_periodc.postMacf)                   1.00     4348     2941
cor(y_num,acf_periodc.postMacf)                       1.00     3233     2627
cor(acf_periodb.acf,acf_periodc.postMacf)             1.00     2885     3130
cor(Intercept,y_num:acf_periodb.acf)                  1.00     4039     2716
cor(y_num,y_num:acf_periodb.acf)                      1.00     3474     2967
cor(acf_periodb.acf,y_num:acf_periodb.acf)            1.00     3227     3122
cor(acf_periodc.postMacf,y_num:acf_periodb.acf)       1.00     3346     3296
cor(Intercept,y_num:acf_periodc.postMacf)             1.00     3021     2605
cor(y_num,y_num:acf_periodc.postMacf)                 1.00     2757     2667
cor(acf_periodb.acf,y_num:acf_periodc.postMacf)       1.00     2628     3042
cor(acf_periodc.postMacf,y_num:acf_periodc.postMacf)  1.00     1975     3043
cor(y_num:acf_periodb.acf,y_num:acf_periodc.postMacf) 1.00     2328     2310

Population-Level Effects: 
                           Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept                     -7.93      0.07    -8.07    -7.78 1.00     1642     2527
y_num                         -0.09      0.01    -0.12    -0.07 1.00     3832     3404
acf_periodb.acf               -0.03      0.98    -1.93     1.93 1.00     2760     2715
acf_periodc.postMacf          -0.35      0.39    -1.11     0.41 1.00     2294     2381
y_num:acf_periodb.acf         -0.01      0.12    -0.26     0.23 1.00     2723     2738
y_num:acf_periodc.postMacf     0.02      0.04    -0.05     0.10 1.00     2115     2245

Family Specific Parameters: 
      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
shape    92.27     66.10    26.85   269.28 1.00     3436     2750

Draws were sampled using sampling(NUTS). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
plot(m_extrapulmonary)

pp_check(m_extrapulmonary, type='ecdf_overlay')
Using 10 posterior draws for ppc type 'ecdf_overlay' by default.

8.2 Summary of change

Summarise in plot

plot_counterfactual(model_data = overall_scaffold %>% filter(year<=1961), model=m_extrapulmonary, 
                    population_denominator = population_without_inst_ship, outcome=inc_100k_extrapulmonary, re_formula = NA)
  
ggsave(here("figures/s6.png"), width=10)
Saving 10 x 4.51 in image

Summarise numerically.


overall_change_extrapulmonary <- summarise_change(model_data=overall_scaffold, model=m_extrapulmonary, 
                                   population_denominator=population_without_inst_ship, grouping_var=NULL, re_formula = NA)
`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.
#want to keep the summary estimates here
tokeep <- c("peak_summary", "level_summary", "slope_summary")

#summary measures in a table
overall_change_extrapulmonary %>%
  keep(names(.) %in% tokeep) %>%
  bind_rows() %>%
  mutate(across(c(estimate:.upper), number, accuracy=0.01)) %>%
  select(measure, everything()) %>%
  datatable()
NA

8.3 Compared to counterfactual

Numbers of extra-pulmonary TB cases averted overall.


overall_ep_counterf <- calculate_counterfactual(model_data = mdata_extrapulmonary, model=m_extrapulmonary, 
                                               population_denominator = population_without_inst_ship)
Joining with `by = join_by(year, population_without_inst_ship, .draw)`Joining with `by = join_by(.draw)`
overall_ep_counterf$counter_post %>%
  mutate(across(c(cases_averted:cases_averted.upper, diff_inc100k:diff_inc100k.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(rr_inc100k:rr_inc100k.upper), number_format(accuracy = 0.01))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  datatable()
NA

Total extrapulmonary TB cases averted between 1958 and 1963


overall_ep_counterf$counter_post_overall %>%
  mutate(across(c(cases_averted:cases_averted.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  datatable()
NA
NA

8.4 Ward-level extra-pulmonary summaries

Ward-level extra-pulmonary estimates in graphical form.


plot_counterfactual(model_data = mdata_extrapulmonary, model=m_extrapulmonary, outcome = inc_100k, 
                    population_denominator = population_without_inst_ship, grouping_var = ward,re_formula =~(y_num*acf_period | ward), 
                    ward)
  
ggsave(here("figures/s4.png"), width=10, height=12)

Numerical summary.


ward_change_extrapulmonary <- summarise_change(model_data = mdata_extrapulmonary, model = m_extrapulmonary, 
                                population_denominator = population_without_inst_ship, grouping_var=ward,
                                re_formula = ~(y_num*acf_period | ward)) 
`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.`summarise()` has grouped output by '.draw', 'acf_period'. You can override using the `.groups` argument.`summarise()` has grouped output by '.draw'. You can override using the `.groups` argument.
#want to keep the summary estimates here
tokeep <- c("peak_summary", "level_summary", "slope_summary")

#summary measures in a table
ward_change_extrapulmonary  %>%
  keep(names(.) %in% tokeep) %>%
  bind_rows() %>%
  mutate(across(c(estimate:.upper), number, accuracy=0.01)) %>%
  select(measure, everything()) %>%
  datatable()
NA
NA
NA

9. Age-sex model

9.1 FIt the model

Fit the model

(Not rewritten the functions for this yet)

Summarise posterior

ggsave(here("figures/s7.png"), height=10)
Saving 7 x 10 in image

9.2 Summary of impact of intervention

Calculate summary effects

level_draws_age_sex <- add_epred_draws(newdata = out_age_sex_2,
                  object = m_age_sex) %>%
    arrange(y_num, .draw) %>%
    group_by(.draw, age, sex) %>%
    summarise(estimate = last(.epred)/first(.epred)) %>%
    ungroup() %>%
    mutate(measure = "RR.level")
`summarise()` has grouped output by '.draw', 'age'. You can override using the `.groups` argument.

Numerica sumamry of these summary results

As a figure


peak_g_age_sex <- peak_summary_age_sex %>%
  mutate(sex = case_when(sex=="M" ~ "Male",
                         sex=="F" ~ "Female")) %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
  ggplot() +
  geom_hline(aes(yintercept=1), linetype=2)+
  geom_pointrange(aes(x=age, y=estimate, ymin=.lower, ymax=.upper, group=sex, colour=sex, shape=sex),
                  position = position_dodge(width = 0.5)) +
  scale_colour_manual(values = c("#CD7AC5", "cadetblue3"), name="") +
  scale_shape(name="") +
  labs(x="",
       y="Relative rate (95% UI)") +
  theme_ggdist() +
  theme(legend.position = "bottom",
        panel.border = element_rect(colour = "grey78", fill=NA))

#level plot
level_g_age_sex <- level_summary_age_sex %>%
  mutate(sex = case_when(sex=="M" ~ "Male",
                         sex=="F" ~ "Female")) %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
  ggplot() +
  geom_hline(aes(yintercept=1), linetype=2)+
  geom_pointrange(aes(x=age, y=estimate, ymin=.lower, ymax=.upper, group=sex, colour=sex, shape=sex),
                  position = position_dodge(width = 0.5)) +
  scale_colour_manual(values = c("#CD7AC5", "cadetblue3"), name="") +
  scale_shape(name="") +
  labs(x="",
       y="Relative rate (95% UI)") +
  theme_ggdist() +
  theme(legend.position = "bottom",
        panel.border = element_rect(colour = "grey78", fill=NA))

#slope plot
slope_g_age_sex <- slope_summary_age_sex %>%
  mutate(sex = case_when(sex=="M" ~ "Male",
                         sex=="F" ~ "Female")) %>%
  mutate(age = case_when(age=="00_05" ~ "0 to 5y",
                         age=="06_15" ~ "06 to 15y",
                         age=="16_25" ~ "16 to 25y",
                         age=="26_35" ~ "26 to 35y",
                         age=="36_45" ~ "36 to 45y",
                         age=="46_55" ~ "46 to 55y",
                         age=="56_65" ~ "56 to 65y",
                         age=="65+" ~ "65 & up y")) %>%
  ggplot() +
  geom_hline(aes(yintercept=1), linetype=2)+
  geom_pointrange(aes(x=age, y=estimate, ymin=.lower, ymax=.upper, group=sex, colour=sex, shape=sex),
                  position = position_dodge(width = 0.5)) +
  scale_colour_manual(values = c("#CD7AC5", "cadetblue3"), name="") +
  scale_shape(name="") +
  labs(x="",
       y="Relative rate (95% UI)") +
  theme_ggdist() +
  theme(legend.position = "bottom",
        panel.border = element_rect(colour = "grey78", fill=NA))

9.3 Compared to counterfactual


counterfact_age_sex <-
      add_epred_draws(object = m_age_sex,
                      newdata = mdata_age_sex %>%
                                    select(year, year2, y_num, age, sex) %>%
                                    mutate(acf_period = "a. pre-acf")) %>%
      filter(year>1957) %>%
      select(year, age, sex, .draw, .epred_counterf = .epred)
Adding missing grouping variables: `year2`, `y_num`, `acf_period`, `.row`
  
#Calcuate predicted number of cases per draw, then summarise.
post_change_age_sex <-
      add_epred_draws(object = m_age_sex,
                      newdata = mdata_age_sex %>%
                                    select(year, year2, y_num, age, sex, acf_period)) %>%
      filter(year>1957) %>%
      ungroup() %>%
      select(year, age, sex, .draw, .epred) 
  
#for the overall period
counterfact_overall_age_sex <-
      add_epred_draws(object = m_age_sex,
                      newdata = mdata_age_sex %>%
                                    select(year, year2, y_num, age, sex) %>%
                                    mutate(acf_period = "a. pre-acf")) %>%
      filter(year>1957) %>%
      select(age, sex, .draw, .epred)  %>%
      group_by(age, sex, .draw) %>%
      summarise(.epred_counterf = sum(.epred)) %>%
      mutate(year = "Overall (1958-1963)")
Adding missing grouping variables: `year`, `year2`, `y_num`, `acf_period`, `.row``summarise()` has grouped output by 'age', 'sex'. You can override using the `.groups` argument.
  
#Calcuate incidence per draw, then summarise.
post_change_overall_age_sex <-
      add_epred_draws(object = m_age_sex,
                      newdata = mdata_age_sex %>%
                                    select(year, year2, y_num, age, sex, acf_period)) %>%
      filter(year>1957) %>%
      select(age, sex, .draw, .epred) %>%
      group_by(.draw, age, sex) %>%
      summarise(.epred = sum(.epred)) 
Adding missing grouping variables: `year`, `year2`, `y_num`, `acf_period`, `.row``summarise()` has grouped output by '.draw', 'age'. You can override using the `.groups` argument.
  
counter_post_overall_age_sex <-
  left_join(counterfact_overall_age_sex, post_change_overall_age_sex) %>%
    mutate(cases_averted = .epred_counterf-.epred,
           pct_change = (.epred - .epred_counterf)/.epred_counterf) %>%
    group_by(age, sex) %>%
    mean_qi(cases_averted, pct_change) %>%
    ungroup() %>%
    mutate(year = "Overall (1958-1963)") 
Joining with `by = join_by(age, sex, .draw)`
age_sex_txt <- counter_post_overall_age_sex %>%
  mutate(across(c(cases_averted:cases_averted.upper), number_format(accuracy = 0.1, big.mark = ","))) %>%
  mutate(across(c(pct_change:pct_change.upper), percent, accuracy=0.1)) %>%
  transmute(year = as.character(year),
            sex = sex,
            age = age,
            cases_averted = glue::glue("{cases_averted}\n({cases_averted.lower} to {cases_averted.upper})"),
            pct_change = glue::glue("{pct_change}\n({pct_change.lower} to {pct_change.upper})"))


age_sex_txt %>% datatable()
NA
NA

Join together for Figure 3.

LS0tCnRpdGxlOiAiR2xhc2dvdyBUQiBBQ0YiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIyAxLiBMaWJyYXJpZXMgYW5kIGZ1bmN0aW9ucwoKIyMjIyAxLjEgTGlicmFyaWVzCgpMb2FkIHRoZSByZXF1aXJlZCBsaWJyYXJpZXMuCgpgYGB7ciwgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHNmKQpsaWJyYXJ5KGhlcmUpCmxpYnJhcnkocmVhZHhsKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShEVCkKbGlicmFyeShicm1zKQpsaWJyYXJ5KHRpZHliYXllcykKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkobWFyZ2luYWxlZmZlY3RzKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoc2NpY28pCmxpYnJhcnkoZ2dkZW5zaXR5KQpsaWJyYXJ5KGdncHVicikKbGlicmFyeSh1bml0cykKI2xpYnJhcnkoZ2dzbikKCmBgYAoKIyMjIyAxLjIgSGVscGVyIGZ1bmN0aW9ucwoKRnVuY3Rpb25zIHRoYXQgd2Ugd2lsbCB1c2UgdGhyb3VnaG91dCB0aGUgc2NyaXB0CgpgYGB7cn0KI2xhYmVsbGVyIGZvciB5ZWFycwp5ZWFyX2xhYmVscyA8LSBjKDE5NTA6MTk2MykKCiNUaGUgR2xhc2dvdyBtYXNzIG1pbnV0dXJlIGNoZXN0IFgtcmF5IGNhbXBhaWduIGhhcHBlbmVkIGJldHdlZW4gMTF0aCBNYXJjaCBhbmQgMTJ0aCBBcHJpbCAxOTU3CiNTZWdtZW50IGZvciBncmFwaHMgdG8gbWF0Y2ggQUNGIHBlcmlvZAphY2Zfc3RhcnQgPC0gZGVjaW1hbF9kYXRlKHltZCgiMTk1Ny0wMy0xMSIpKQphY2ZfZW5kIDwtIGRlY2ltYWxfZGF0ZSh5bWQoIjE5NTctMDQtMTIiKSkKCgpgYGAKCkZ1bmN0aW9uIGZvciBjb3VudGVyZmFjdHVhbCBwbG90cwoKYGBge3J9CgoKcGxvdF9jb3VudGVyZmFjdHVhbCA8LSBmdW5jdGlvbihtb2RlbF9kYXRhLCBtb2RlbCwgcG9wdWxhdGlvbl9kZW5vbWluYXRvciwgb3V0Y29tZSwgZ3JvdXBpbmdfdmFyPU5VTEwsIHJlX2Zvcm11bGEsLi4uKXsKICAKICAjbGFiZWxsZXIgZm9yIHllYXJzCiAgeWVhcl9sYWJlbHMgPC0gYygxOTUwOjE5NjMpCgogICNUaGUgR2xhc2dvdyBtYXNzIG1pbnV0dXJlIGNoZXN0IFgtcmF5IGNhbXBhaWduIGhhcHBlbmVkIGJldHdlZW4gMTF0aCBNYXJjaCBhbmQgMTJ0aCBBcHJpbCAxOTU3CiAgI1NlZ21lbnQgZm9yIGdyYXBocyB0byBtYXRjaCBBQ0YgcGVyaW9kCiAgYWNmX3N0YXJ0IDwtIGRlY2ltYWxfZGF0ZSh5bWQoIjE5NTctMDMtMTEiKSkKICBhY2ZfZW5kIDwtIGRlY2ltYWxfZGF0ZSh5bWQoIjE5NTctMDQtMTIiKSkKCiAgc3VtbWFyeSA8LSB7e21vZGVsX2RhdGF9fSAlPiUKICAgIHNlbGVjdCh5ZWFyLCB5ZWFyMiwgeV9udW0sIGFjZl9wZXJpb2QsIHt7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19LCB7e291dGNvbWV9fSwge3tncm91cGluZ192YXJ9fSkgJT4lCiAgICBhZGRfZXByZWRfZHJhd3Moe3ttb2RlbH19LCByZV9mb3JtdWxhPXt7cmVfZm9ybXVsYX19KSAlPiUKICAgIGdyb3VwX2J5KHllYXIyLCBhY2ZfcGVyaW9kLCB7e2dyb3VwaW5nX3Zhcn19KSAlPiUKICAgIG1lYW5fcWkoKSAlPiUKICAgIG11dGF0ZSguZXByZWRfaW5jID0gLmVwcmVkL3t7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19KjEwMDAwMCwKICAgICAgICAgIC5lcHJlZF9pbmMubG93ZXIgPSAuZXByZWQubG93ZXIve3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0qMTAwMDAwLAogICAgICAgICAgLmVwcmVkX2luYy51cHBlciA9IC5lcHJlZC51cHBlci97e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSoxMDAwMDApICU+JQogICAgbXV0YXRlKGFjZl9wZXJpb2QgPSBjYXNlX3doZW4oYWNmX3BlcmlvZD09ImEuIHByZS1hY2YiIH4gIkJlZm9yZSBJbnRlcnZlbnRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWNmX3BlcmlvZD09ImMuIHBvc3QtYWNmIiB+ICJQb3N0IEludGVydmVudGlvbiIpKQoKCgogICNjcmVhdGUgdGhlIGNvdW50ZXJmYWN0dWFsIChubyBpbnRlcnZlbnRpb24pLCBhbmQgc3VtbWFyaXNlCiAgCiAgY291bnRlcmZhY3QgPC0KICAgIGFkZF9lcHJlZF9kcmF3cyhvYmplY3QgPSB7e21vZGVsfX0sCiAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IHt7bW9kZWxfZGF0YX19ICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHllYXIsIHllYXIyLCB5X251bSwge3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0sIHt7Z3JvdXBpbmdfdmFyfX0sIHt7b3V0Y29tZX19KSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShhY2ZfcGVyaW9kID0gImEuIHByZS1hY2YiKSwgcmVfZm9ybXVsYT17e3JlX2Zvcm11bGF9fSkgJT4lCiAgICBncm91cF9ieSh5ZWFyMiwgYWNmX3BlcmlvZCwge3tncm91cGluZ192YXJ9fSkgJT4lCiAgICBtZWFuX3FpKCkgJT4lCiAgICBtdXRhdGUoLmVwcmVkX2luYyA9IC5lcHJlZC97e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSoxMDAwMDAsCiAgICAgICAgIC5lcHJlZF9pbmMubG93ZXIgPSAuZXByZWQubG93ZXIve3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0qMTAwMDAwLAogICAgICAgICAuZXByZWRfaW5jLnVwcGVyID0gLmVwcmVkLnVwcGVyL3t7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19KjEwMDAwMCkgJT4lCiAgICBtdXRhdGUoYWNmX3BlcmlvZCA9IGNhc2Vfd2hlbihhY2ZfcGVyaW9kPT0iYS4gcHJlLWFjZiIgfiAiQmVmb3JlIEludGVydmVudGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWNmX3BlcmlvZD09ImMuIHBvc3QtYWNmIiB+ICJQb3N0IEludGVydmVudGlvbiIpKQogIAoKCiAgI3Bsb3QgdGhlIGludGVydmVudGlvbiBlZmZlY3QKcCA8LSBzdW1tYXJ5ICU+JQogICAgZHJvcGxldmVscygpICU+JQogICAgZ2dwbG90KCkgKwogICAgZ2VvbV9yaWJib24oYWVzKHltaW49LmVwcmVkX2luYy5sb3dlciwgeW1heD0uZXByZWRfaW5jLnVwcGVyLCB4PXllYXIyLCBncm91cCA9IGFjZl9wZXJpb2QsIGZpbGw9YWNmX3BlcmlvZCksIGFscGhhPTAuNSkgKwogICAgZ2VvbV9yaWJib24oZGF0YSA9IGNvdW50ZXJmYWN0ICU+JSBmaWx0ZXIoeWVhcj49MTk1NiksIAogICAgICAgICAgICAgICAgYWVzKHltaW49LmVwcmVkX2luYy5sb3dlciwgeW1heD0uZXByZWRfaW5jLnVwcGVyLCB4PXllYXIyLCBmaWxsPSJDb3VudGVyZmFjdHVhbCIpLCBhbHBoYT0wLjUpICsKICAgIGdlb21fbGluZShkYXRhID0gY291bnRlcmZhY3QgJT4lIGZpbHRlcih5ZWFyPj0xOTU2KSwgCiAgICAgICAgICAgICAgYWVzKHk9LmVwcmVkX2luYywgeD15ZWFyMiwgY29sb3VyPSJDb3VudGVyZmFjdHVhbCIpKSArCiAgICBnZW9tX2xpbmUoYWVzKHk9LmVwcmVkX2luYywgeD15ZWFyMiwgZ3JvdXA9YWNmX3BlcmlvZCwgIGNvbG91cj1hY2ZfcGVyaW9kKSkgKwogICAgZ2VvbV9wb2ludChkYXRhID0ge3ttb2RlbF9kYXRhfX0sIGFlcyh5PXt7b3V0Y29tZX19LCB4PXllYXIyLCBzaGFwZT1hY2ZfcGVyaW9kKSwgc2l6ZT0yKSArCiAgICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9lbmQpLCBsaW5ldHlwZT0zKSArCiAgICB0aGVtZV9nZ2Rpc3QoKSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hLCBsaW1pdHMgPSBjKDAsTkEpKSArCiAgICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0geWVhcl9sYWJlbHMpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiNERTBEOTIiLCAiZ3JleTUwIiwgIiM0RDZDRkEiKSAsIG5hbWU9IiIsIG5hLnRyYW5zbGF0ZSA9IEYpICsKICAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiI0RFMEQ5MiIsICJncmV5NTAiLCAiIzRENkNGQSIpICwgbmFtZT0iIiwgbmEudHJhbnNsYXRlID0gRikgKwogICAgc2NhbGVfc2hhcGVfZGlzY3JldGUobmFtZT0iIiwgbmEudHJhbnNsYXRlID0gRikgKwogICAgbGFicygKICAgICAgeCA9ICJZZWFyIiwKICAgICAgeSA9ICJDYXNlIG5vdGlmaWNhdGlvbiByYXRlIChwZXIgMTAwLDAwMCkiLAogICAgICBjYXB0aW9uID0gIk1hc3MgbWluaWF0dXJlIFgtcmF5IGNhbXBhaWduIHBlcmlvZCBiZXR3ZWVuIGRhc2hlZCBsaW5lcyAoMTF0aCBNYXJjaC0xMnRoIEFwcmlsIDE5NTcpIgogICAgKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSksCiAgICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTgsIGFuZ2xlID0gOTAsIGhqdXN0PTEsIHZqdXN0PTAuNSksCiAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTgpKSArCiAgICBndWlkZXMoc2hhcGU9Im5vbmUiKQoKICAgIGZhY2V0X3ZhcnMgPC0gdmFycyguLi4pCgogIGlmIChsZW5ndGgoZmFjZXRfdmFycykgIT0gMCkgewogICAgcCA8LSBwICsgZmFjZXRfd3JhcChmYWNldF92YXJzKQogIH0KICBwCgp9CgpgYGAKCkZ1bmN0aW9uIGZvciBjYWxjdWxhdGluZyAgbWVhc3VyZXMgb2YgY2hhbmdlIG92ZXIgdGltZSAoUlIucGVhaywgUlIubGV2ZWwsIFJSLnNsb3BlKQoKCmBgYHtyfQoKc3VtbWFyaXNlX2NoYW5nZSA8LSBmdW5jdGlvbihtb2RlbF9kYXRhLCBtb2RlbCwgcG9wdWxhdGlvbl9kZW5vbWluYXRvciwgZ3JvdXBpbmdfdmFyID0gTlVMTCwgcmVfZm9ybXVsYSA9IE5VTEwpIHsKICAKICAjZnVuY3Rpb25zIGZvciBjYWxjdWxhdGluZyBSUi5wZWFrCiAgI2kuZS4gcmVsYXRpdmUgY2FzZSBub3RpZmljYXRpb24gcmF0ZSBpbiAxOTU3IHZzLiBjb3VudGVyZmFjdHVhbCB0cmVuZCBmb3IgMTk1NwogIAogIGdyb3VwaW5nX3ZhciA8LSBlbnF1byhncm91cGluZ192YXIpCiAgCiAgaWYgKCFpcy5udWxsKHt7Z3JvdXBpbmdfdmFyfX0pKSB7CiAgICAKICAgICNtYWtlIHRoZSBwcmVkaWN0aW9uIG1hdHJpeCwgY29uZGl0aW9uYWwgb24gd2hldGhlciB3ZSB3YW50IHJhbmRvbSBlZmZlY3RzIGluY2x1ZGVkIG9yIG5vdC4KICAgIG91dCA8LSBjcm9zc2luZyh7e21vZGVsX2RhdGF9fSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICBzZWxlY3Qoe3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0sIHlfbnVtLCAhIWdyb3VwaW5nX3ZhcikgJT4lCiAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoeV9udW0gPT0gOCksCiAgICAgICAgICAgICAgICAgICAgYWNmX3BlcmlvZCA9IGMoImEuIHByZS1hY2YiLCAiYi4gYWNmIikKICAgICkKICB9IGVsc2UgewogICAgCiAgICBvdXQgPC0gY3Jvc3Npbmcoe3ttb2RlbF9kYXRhfX0gJT4lIAogICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHt7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19LCB5X251bSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoeV9udW0gPT0gOCksCiAgICAgICAgICAgICAgICAgICAgYWNmX3BlcmlvZCA9IGMoImEuIHByZS1hY2YiLCAiYi4gYWNmIikKICAgICkKICB9CiAgCiAgcGVha19kcmF3cyA8LSBhZGRfZXByZWRfZHJhd3MobmV3ZGF0YSA9IG91dCwKICAgICAgICAgICAgICAgICAgb2JqZWN0ID0ge3ttb2RlbH19LAogICAgICAgICAgICAgICAgICByZV9mb3JtdWxhID0ge3tyZV9mb3JtdWxhfX0pICU+JQogICAgbXV0YXRlKGVwcmVkX2NuciA9IC5lcHJlZC9wb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwKjEwMDAwMCkgJT4lCiAgICBncm91cF9ieSguZHJhdywgISFncm91cGluZ192YXIpICU+JQogICAgc3VtbWFyaXNlKGVzdGltYXRlID0gbGFzdChlcHJlZF9jbnIpL2ZpcnN0KGVwcmVkX2NucikpICU+JQogICAgdW5ncm91cCgpICU+JQogICAgbXV0YXRlKG1lYXN1cmUgPSAiUlIucGVhayIpCiAgCiAgcGVha19zdW1tYXJ5IDwtIHBlYWtfZHJhd3MgJT4lCiAgICBncm91cF9ieSghIWdyb3VwaW5nX3ZhcikgJT4lCiAgICBtZWFuX3FpKGVzdGltYXRlKSAlPiUKICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLnBlYWsiKQogIAogIAogICNmdW5jdGlvbnMgZm9yIGNhbGN1bGF0aW5nIFJSLnN0ZXAKICAjaS5lLiByZWxhdGl2ZSBjYXNlIG5vdGlmaWNhdGlvbiByYXRlIGluIDE5NTggdnMuIGNvdW50ZXJmYWN0dWFsIHRyZW5kIGZvciAxOTU4CiAgCiAgICBpZiAoIWlzLm51bGwoe3tncm91cGluZ192YXJ9fSkpIHsKICAgIG91dDIgPC0gY3Jvc3Npbmcoe3ttb2RlbF9kYXRhfX0gJT4lIAogICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHt7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19LCB5X251bSwgISFncm91cGluZ192YXIpICU+JQogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHlfbnVtID09IDkpLAogICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2QgPSBjKCJhLiBwcmUtYWNmIiwgImMuIHBvc3QtYWNmIikKICAgICkKICB9IGVsc2UgewogICAgCiAgICBvdXQyIDwtIGNyb3NzaW5nKHt7bW9kZWxfZGF0YX19ICU+JSAKICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCh7e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSwgeV9udW0pICU+JQogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHlfbnVtID09IDkpLAogICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2QgPSBjKCJhLiBwcmUtYWNmIiwgImMuIHBvc3QtYWNmIikKICAgICkKICB9CiAgCiAgICBsZXZlbF9kcmF3cyA8LSBhZGRfZXByZWRfZHJhd3MobmV3ZGF0YSA9IG91dDIsCiAgICAgICAgICAgICAgICAgIG9iamVjdCA9IHt7bW9kZWx9fSwKICAgICAgICAgICAgICAgICAgcmVfZm9ybXVsYSA9IHt7cmVfZm9ybXVsYX19KSAlPiUKICAgIGFycmFuZ2UoeV9udW0sIC5kcmF3KSAlPiUKICAgIG11dGF0ZShlcHJlZF9jbnIgPSAuZXByZWQvcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCoxMDAwMDApICU+JQogICAgZ3JvdXBfYnkoLmRyYXcsICEhZ3JvdXBpbmdfdmFyKSAlPiUKICAgIHN1bW1hcmlzZShlc3RpbWF0ZSA9IGxhc3QoZXByZWRfY25yKS9maXJzdChlcHJlZF9jbnIpKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLmxldmVsIikKICAKICBsZXZlbF9zdW1tYXJ5IDwtIGxldmVsX2RyYXdzICU+JQogICAgZ3JvdXBfYnkoISFncm91cGluZ192YXIpICU+JQogICAgbWVhbl9xaShlc3RpbWF0ZSkgJT4lCiAgICBtdXRhdGUobWVhc3VyZSA9ICJSUi5sZXZlbCIpCiAgICAKICAgIAogICNmdW5jdGlvbnMgZm9yIGNhbGN1bGF0aW5nIFJSLnNsb3BlCiAgI2kuZS4gcmVsYXRpdmUgY2hhbmdlIGluIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGUgaW4gMTk1OC0xOTYzIHZzLiBjb3VudGVyZmFjdHVhbCB0cmVuZCBmb3IgMTk1OS0xOTYzCiAgCiAgICBpZiAoIWlzLm51bGwoe3tncm91cGluZ192YXJ9fSkpIHsKICAgIG91dDMgPC0gY3Jvc3Npbmcoe3ttb2RlbF9kYXRhfX0gJT4lIAogICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHt7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19LCB5X251bSwgISFncm91cGluZ192YXIpICU+JQogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHlfbnVtICVpbiUgYyg5LDE0KSksCiAgICAgICAgICAgICAgICAgICAgYWNmX3BlcmlvZCA9IGMoImEuIHByZS1hY2YiLCAiYy4gcG9zdC1hY2YiKQogICAgKQogIH0gZWxzZSB7CiAgICAKICAgIG91dDMgPC0gY3Jvc3Npbmcoe3ttb2RlbF9kYXRhfX0gJT4lIAogICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHt7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19LCB5X251bSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoeV9udW0gJWluJSBjKDksMTQpKSwKICAgICAgICAgICAgICAgICAgICBhY2ZfcGVyaW9kID0gYygiYS4gcHJlLWFjZiIsICJjLiBwb3N0LWFjZiIpCiAgICApCiAgfQogIAogICAgc2xvcGVfZHJhd3MgPC0gYWRkX2VwcmVkX2RyYXdzKG5ld2RhdGEgPSBvdXQzLAogICAgICAgICAgICAgICAgICBvYmplY3QgPSB7e21vZGVsfX0sCiAgICAgICAgICAgICAgICAgIHJlX2Zvcm11bGEgPSB7e3JlX2Zvcm11bGF9fSkgJT4lCiAgICAgICAgYXJyYW5nZSh5X251bSkgJT4lCiAgICAgICAgdW5ncm91cCgpICU+JQogICAgICAgIG11dGF0ZShlcHJlZF9jbnIgPSAuZXByZWQvcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCoxMDAwMDApICU+JQogICAgICAgIGdyb3VwX2J5KC5kcmF3LCBhY2ZfcGVyaW9kLCAhIWdyb3VwaW5nX3ZhcikgJT4lCiAgICAgICAgc3VtbWFyaXNlKHNsb3BlID0gKGxhc3QoZXByZWRfY25yKSAtIGZpcnN0KGVwcmVkX2NucikpIC8gKGxhc3QoeV9udW0pLWZpcnN0KHlfbnVtKSkpICU+JQogICAgICAgIHVuZ3JvdXAoKSAlPiUKICAgICAgICBncm91cF9ieSguZHJhdywgISFncm91cGluZ192YXIpICU+JQogICAgICAgIHN1bW1hcmlzZShlc3RpbWF0ZSA9IGxhc3Qoc2xvcGUpL2ZpcnN0KHNsb3BlKSkgJT4lCiAgICAgICAgbXV0YXRlKG1lYXN1cmUgPSAiUlIuc2xvcGUiKQogIAogIHNsb3BlX3N1bW1hcnkgPC0gc2xvcGVfZHJhd3MgJT4lCiAgICAgZ3JvdXBfYnkoISFncm91cGluZ192YXIpICU+JQogICAgICBtZWRpYW5fcWkoZXN0aW1hdGUpICU+JQogICAgICBtdXRhdGUobWVhc3VyZSA9ICJSUi5zbG9wZSIpCiAgICAKICAjZ2F0aGVyIGFsbCB0aGUgcmVzdWx0cyBpbnRvIGEgbmFtZWQgbGlzdAogICAgbHN0KHBlYWtfZHJhd3M9cGVha19kcmF3cywgcGVha19zdW1tYXJ5PXBlYWtfc3VtbWFyeSwgCiAgICAgICAgbGV2ZWxfZHJhd3M9bGV2ZWxfZHJhd3MsIGxldmVsX3N1bW1hcnk9bGV2ZWxfc3VtbWFyeSwgCiAgICAgICAgc2xvcGVfZHJhd3M9c2xvcGVfZHJhd3MsIHNsb3BlX3N1bW1hcnk9c2xvcGVfc3VtbWFyeSkKICAKfQoKYGBgCgoKCkZ1bmN0aW9uIGZvciBjYWxjdWxhdGluZyBkaWZmZXJlbmNlIGZyb20gY291bnRlcmZhY3R1YWwKCmBgYHtyfQoKY2FsY3VsYXRlX2NvdW50ZXJmYWN0dWFsIDwtIGZ1bmN0aW9uKG1vZGVsX2RhdGEsIG1vZGVsLCBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yLCBncm91cGluZ192YXI9TlVMTCwgcmVfZm9ybXVsYT1OQSl7CiAgCiAgI2VmZmVjdCB2cy4gY291bnRlcmZhY3R1YWwKICBjb3VudGVyZmFjdCA8LQogICAgICBhZGRfZXByZWRfZHJhd3Mob2JqZWN0ID0ge3ttb2RlbH19LAogICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IHt7bW9kZWxfZGF0YX19ICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoeWVhciwgeWVhcjIsIHlfbnVtLCB7e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSwge3tncm91cGluZ192YXJ9fSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShhY2ZfcGVyaW9kID0gImEuIHByZS1hY2YiKSwKICAgICAgICAgICAgICAgICAgICAgIHJlX2Zvcm11bGEgPSB7e3JlX2Zvcm11bGF9fSkgJT4lCiAgICAgIGdyb3VwX2J5KC5kcmF3LCB5ZWFyLCB7e2dyb3VwaW5nX3Zhcn19LCBhY2ZfcGVyaW9kKSAlPiUKICAgICAgbXV0YXRlKC5lcHJlZF9pbmNfY291bnRlcmYgPSAuZXByZWQve3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0qMTAwMDAwLCAuZXByZWRfY291bnRlcmY9LmVwcmVkKSAgJT4lCiAgICAgIGZpbHRlcih5ZWFyPjE5NTcpICU+JQogICAgICB1bmdyb3VwKCkgJT4lCiAgICAgIHNlbGVjdCh5ZWFyLCB7e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSwgLmRyYXcsIC5lcHJlZF9jb3VudGVyZiwgLmVwcmVkX2luY19jb3VudGVyZiwge3tncm91cGluZ192YXJ9fSkKICAKICAjQ2FsY3VhdGUgY2FzZSBub3RpZmljYXRpb24gcmF0ZSBwZXIgZHJhdywgdGhlbiBzdW1tYXJpc2UuCiAgcG9zdF9jaGFuZ2UgPC0KICAgICAgYWRkX2VwcmVkX2RyYXdzKG9iamVjdCA9IHt7bW9kZWx9fSwKICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSB7e21vZGVsX2RhdGF9fSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHllYXIsIHllYXIyLCB5X251bSwge3twb3B1bGF0aW9uX2Rlbm9taW5hdG9yfX0sIHt7Z3JvdXBpbmdfdmFyfX0sIGFjZl9wZXJpb2QpLAogICAgICAgICAgICAgICAgICAgICAgcmVfZm9ybXVsYSA9IHt7cmVfZm9ybXVsYX19KSAlPiUKICAgICAgZ3JvdXBfYnkoLmRyYXcsIHllYXIsIHt7Z3JvdXBpbmdfdmFyfX0sIGFjZl9wZXJpb2QpICU+JQogICAgICBtdXRhdGUoLmVwcmVkX2luYyA9IC5lcHJlZC97e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSoxMDAwMDApICAlPiUKICAgICAgZmlsdGVyKHllYXI+MTk1NykgJT4lCiAgICAgIHVuZ3JvdXAoKSAlPiUKICAgICAgc2VsZWN0KHllYXIsIHt7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19LCB7e2dyb3VwaW5nX3Zhcn19LCAuZHJhdywgLmVwcmVkLCAuZXByZWRfaW5jLCB7e2dyb3VwaW5nX3Zhcn19KSAKICAKICAjZm9yIHRoZSBvdmVyYWxsIHBlcmlvZAogICAgY291bnRlcmZhY3Rfb3ZlcmFsbCA8LQogICAgICBhZGRfZXByZWRfZHJhd3Mob2JqZWN0ID0ge3ttb2RlbH19LAogICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IHt7bW9kZWxfZGF0YX19ICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoeWVhciwgeWVhcjIsIHlfbnVtLCB7e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSwge3tncm91cGluZ192YXJ9fSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShhY2ZfcGVyaW9kID0gImEuIHByZS1hY2YiKSwKICAgICAgICAgICAgICAgICAgICAgIHJlX2Zvcm11bGEgPSB7e3JlX2Zvcm11bGF9fSkgJT4lCiAgICAgIGdyb3VwX2J5KC5kcmF3LCB7e2dyb3VwaW5nX3Zhcn19KSAlPiUKICAgICAgZmlsdGVyKHllYXI+MTk1NykgJT4lCiAgICAgIHVuZ3JvdXAoKSAlPiUKICAgICAgc2VsZWN0KHt7cG9wdWxhdGlvbl9kZW5vbWluYXRvcn19LCAuZHJhdywgLmVwcmVkLCB7e2dyb3VwaW5nX3Zhcn19KSAgJT4lCiAgICAgIGdyb3VwX2J5KC5kcmF3LCB7e2dyb3VwaW5nX3Zhcn19KSAlPiUKICAgICAgc3VtbWFyaXNlKC5lcHJlZF9jb3VudGVyZiA9IHN1bSguZXByZWQpKSAKICAKICAjQ2FsY3VhdGUgY2FzZSBub3RpZmljYXRpb24gcmF0ZSBwZXIgZHJhdywgdGhlbiBzdW1tYXJpc2UuCiAgcG9zdF9jaGFuZ2Vfb3ZlcmFsbCA8LQogICAgICBhZGRfZXByZWRfZHJhd3Mob2JqZWN0ID0ge3ttb2RlbH19LAogICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IHt7bW9kZWxfZGF0YX19ICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoeWVhciwgeWVhcjIsIHlfbnVtLCB7e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSwge3tncm91cGluZ192YXJ9fSwgYWNmX3BlcmlvZCksCiAgICAgICAgICAgICAgICAgICAgICByZV9mb3JtdWxhID0ge3tyZV9mb3JtdWxhfX0pICU+JQogICAgICBncm91cF9ieSguZHJhdywge3tncm91cGluZ192YXJ9fSkgJT4lCiAgICAgIGZpbHRlcih5ZWFyPjE5NTcpICU+JQogICAgICB1bmdyb3VwKCkgJT4lCiAgICAgIHNlbGVjdCh7e3BvcHVsYXRpb25fZGVub21pbmF0b3J9fSwge3tncm91cGluZ192YXJ9fSwgLmRyYXcsIC5lcHJlZCkgJT4lCiAgICAgIGdyb3VwX2J5KC5kcmF3LCB7e2dyb3VwaW5nX3Zhcn19KSAlPiUKICAgICAgc3VtbWFyaXNlKC5lcHJlZCA9IHN1bSguZXByZWQpKSAKICAKICAKY291bnRlcl9wb3N0IDwtCiAgbGVmdF9qb2luKGNvdW50ZXJmYWN0LCBwb3N0X2NoYW5nZSkgJT4lCiAgICBtdXRhdGUoY2FzZXNfYXZlcnRlZCA9IC5lcHJlZF9jb3VudGVyZi0uZXByZWQsCiAgICAgICAgICAgcGN0X2NoYW5nZSA9ICguZXByZWQgLSAuZXByZWRfY291bnRlcmYpLy5lcHJlZF9jb3VudGVyZiwKICAgICAgICAgICBkaWZmX2luYzEwMGsgPSAuZXByZWRfaW5jIC0gLmVwcmVkX2luY19jb3VudGVyZiwKICAgICAgICAgICBycl9pbmMxMDBrID0gLmVwcmVkX2luYy8uZXByZWRfaW5jX2NvdW50ZXJmKSAlPiUKICAgIGdyb3VwX2J5KHllYXIsIHt7Z3JvdXBpbmdfdmFyfX0pICU+JQogICAgbWVhbl9xaShjYXNlc19hdmVydGVkLCBwY3RfY2hhbmdlLCBkaWZmX2luYzEwMGssIHJyX2luYzEwMGspICU+JQogICAgdW5ncm91cCgpCgpjb3VudGVyX3Bvc3Rfb3ZlcmFsbCA8LQogIGxlZnRfam9pbihjb3VudGVyZmFjdF9vdmVyYWxsLCBwb3N0X2NoYW5nZV9vdmVyYWxsKSAlPiUKICAgIG11dGF0ZShjYXNlc19hdmVydGVkID0gLmVwcmVkX2NvdW50ZXJmLS5lcHJlZCwKICAgICAgICAgICBwY3RfY2hhbmdlID0gKC5lcHJlZCAtIC5lcHJlZF9jb3VudGVyZikvLmVwcmVkX2NvdW50ZXJmKSAlPiUKICAgIGdyb3VwX2J5KHt7Z3JvdXBpbmdfdmFyfX0pICU+JQogICAgbWVhbl9xaShjYXNlc19hdmVydGVkLCBwY3RfY2hhbmdlKSAlPiUKICAgIHVuZ3JvdXAoKQoKbHN0KGNvdW50ZXJfcG9zdCwgY291bnRlcl9wb3N0X292ZXJhbGwpCgp9CgoKYGBgCgpGdW5jdGlvbiBmb3IgdGlkeWluZyB1cCBjb3VudGVyZmFjdHVhbHMgKG1vc3RseSBmb3IgbWFraW5nIG5pY2UgdGFibGVzKQoKYGBge3J9Cgp0aWR5X2NvdW50ZXJmYWN0dWFscyA8LSBmdW5jdGlvbihkYXRhKXsKICBkYXRhICU+JQogIG11dGF0ZShhY3Jvc3MoYyhjYXNlc19hdmVydGVkOmNhc2VzX2F2ZXJ0ZWQudXBwZXIsIGRpZmZfaW5jMTAwazpkaWZmX2luYzEwMGsudXBwZXIpLCBudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4xLCBiaWcubWFyayA9ICIsIikpKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMocnJfaW5jMTAwazpycl9pbmMxMDBrLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMDEpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHBjdF9jaGFuZ2U6cGN0X2NoYW5nZS51cHBlciksIHBlcmNlbnQsIGFjY3VyYWN5PTAuMSkpICU+JQogIG11dGF0ZSh5ZWFyID0gYXMuY2hhcmFjdGVyKHllYXIpLAogICAgICAgICAgICBjYXNlc19hdmVydGVkID0gZ2x1ZTo6Z2x1ZSgie2Nhc2VzX2F2ZXJ0ZWR9ICh7Y2FzZXNfYXZlcnRlZC5sb3dlcn0gdG8ge2Nhc2VzX2F2ZXJ0ZWQudXBwZXJ9KSIpLAogICAgICAgICAgICBwY3RfY2hhbmdlID0gZ2x1ZTo6Z2x1ZSgie3BjdF9jaGFuZ2V9ICh7cGN0X2NoYW5nZS5sb3dlcn0gdG8ge3BjdF9jaGFuZ2UudXBwZXJ9KSIpLAogICAgICAgICAgICBkaWZmX2luYyA9IGdsdWU6OmdsdWUoIntkaWZmX2luYzEwMGt9ICh7ZGlmZl9pbmMxMDBrLmxvd2VyfSB0byB7ZGlmZl9pbmMxMDBrLnVwcGVyfSkiKSwKICAgICAgICAgICAgcnJfaW5jID0gZ2x1ZTo6Z2x1ZSgie3JyX2luYzEwMGt9ICh7cnJfaW5jMTAway5sb3dlcn0gdG8ge3JyX2luYzEwMGsudXBwZXJ9KSIpKQp9CgoKdGlkeV9jb3VudGVyZmFjdHVhbHNfb3ZlcmFsbCA8LSBmdW5jdGlvbihkYXRhKXsKICBkYXRhICU+JQogIG11dGF0ZShhY3Jvc3MoYyhjYXNlc19hdmVydGVkOmNhc2VzX2F2ZXJ0ZWQudXBwZXIpLCBudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4xLCBiaWcubWFyayA9ICIsIikpKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMocGN0X2NoYW5nZTpwY3RfY2hhbmdlLnVwcGVyKSwgcGVyY2VudCwgYWNjdXJhY3k9MC4xKSkgJT4lCiAgbXV0YXRlKHllYXIgPSBhcy5jaGFyYWN0ZXIoeWVhciksCiAgICAgICAgICAgIGNhc2VzX2F2ZXJ0ZWQgPSBnbHVlOjpnbHVlKCJ7Y2FzZXNfYXZlcnRlZH0gKHtjYXNlc19hdmVydGVkLmxvd2VyfSB0byB7Y2FzZXNfYXZlcnRlZC51cHBlcn0pIiksCiAgICAgICAgICAgIHBjdF9jaGFuZ2UgPSBnbHVlOjpnbHVlKCJ7cGN0X2NoYW5nZX0gKHtwY3RfY2hhbmdlLmxvd2VyfSB0byB7cGN0X2NoYW5nZS51cHBlcn0pIikpCn0KCmBgYAoKCgojIyMgMi4gRGF0YQoKSW1wb3J0IGRhdGFzZXRzIGZvciBhbmFseXNpcwoKIyMjIyAyLjEgU2hhcGVmaWxlcwoKTWFrZSBhIG1hcCBvZiBHbGFzZ293IHdhcmRzCgpgYGB7cn0KCmdsYXNnb3dfd2FyZHNfMTk1MSA8LSBzdF9yZWFkKGhlcmUoIm1hcHBpbmcvZ2xhc2dvd193YXJkc18xOTUxLmdlb2pzb24iKSkKCmBgYAoKYGBge3J9CgojcmVhZCBpbiBTY290bGFuZCBib3VuZGFyeQpzY290bGFuZCA8LSBzdF9yZWFkKGhlcmUoIm1hcHBpbmcvU2NvdGxhbmRfYm91bmRhcnkvU2NvdGxhbmQgYm91bmRhcnkuc2hwIikpCgojbWFrZSBhIGJvdW5kaW5nIGJveCBmb3IgR2xhc2dvdwpiYm94IDwtIHN0X2Jib3goZ2xhc2dvd193YXJkc18xOTUxKSB8PiBzdF9hc19zZmMoKQoKI3Bsb3Qgc2NvdGxhbmQgd2l0aCBhIGJvdW5kaW5nIGJveCBhcm91bmQgdGhlIENpdHkgb2YgR2xhc2dvdwpzY290bGFuZF93aXRoX2Jib3ggPC0gZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YSA9IHNjb3RsYW5kLCBmaWxsPSJhbnRpcXVld2hpdGUiKSArCiAgZ2VvbV9zZihkYXRhID0gYmJveCwgY29sb3VyID0gIiNDNjBDMzAiLCBmaWxsPSJhbnRpcXVld2hpdGUiKSArCiAgdGhlbWVfdm9pZCgpICsKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXk3OCIsIGZpbGw9TkEsIGxpbmV3aWR0aCA9IDAuNSksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIiNFQUY3RkEiLCBzaXplID0gMC4zKSkKCiNwbG90IHRoZSB3YXJkcwojbm90ZSB3ZSB0aWR5IHVwIHNvbWUgbmFtZXMgdG8gZml0IG9uIG1hcApnbGFzZ293X3dhcmRfbWFwIDwtIGdsYXNnb3dfd2FyZHNfMTk1MSAlPiUKICBtdXRhdGUod2FyZCA9IGNhc2Vfd2hlbih3YXJkPT0iU2hldHRsZXN0b24gYW5kIFRvbGxjcm9zcyIgfiAiU2hldHRsZXN0b24gYW5kXG5Ub2xsY3Jvc3MiLAogICAgICAgICAgICAgICAgICAgICAgICAgIHdhcmQ9PSJQYXJ0aWNrIChXZXN0KSIgfiAiUGFydGlja1xuKFdlc3QpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICB3YXJkPT0iUGFydGljayAoRWFzdCkiIH4gIlBhcnRpY2tcbihFYXN0KSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgd2FyZD09Ik5vcnRoIEtlbHZpbiIgfiAiTm9ydGhcbktlbHZpbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgd2FyZD09Iktpbm5pbmcgUGFyayIgfiAiS2lubmluZ1xuUGFyayIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IHdhcmQpKSAlPiUKICAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9zZihhZXMoZmlsbD1kaXZpc2lvbikpICsKICBnZW9tX3NmX2xhYmVsKGFlcyhsYWJlbCA9IHdhcmQpLCBzaXplPTMsIGZpbGw9TkEsIGxhYmVsLnNpemUgPSBOQSwgY29sb3VyPSJibGFjayIpICsKICAjc2NhbGVfY29sb3VyX2lkZW50aXR5KCkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MyIsIG5hbWU9IkNpdHkgb2YgR2xhc2dvdyBEaXZpc2lvbiIpICsKICB0aGVtZV9ncmV5KCkgKwogIGxhYnMoeD0iIiwKICAgICAgIHk9IiIsCiAgICAgICBmaWxsPSJEaXZpc2lvbiIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIiwKICAgICAgICAKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXk3OCIsIGZpbGw9TkEsIGxpbmV3aWR0aCA9IDAuNSksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImFudGlxdWV3aGl0ZSIsIHNpemUgPSAwLjMpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JleTc4IikpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQodGl0bGUucG9zaXRpb24gPSAidG9wIiwgdGl0bGUuaGp1c3QgPSAwLjUsIHRpdGxlLnRoZW1lID0gZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiKSkpCgojYWRkIHRoZSBtYXAgb2Ygc2NvdGxhbmQgYXMgYW4gaW5zZXQKZ2xhc2dvd193YXJkX21hcCArIGluc2V0X2VsZW1lbnQoc2NvdGxhbmRfd2l0aF9iYm94LCAwLjc1LCAwLCAxLCAwLjQpCgpnZ3NhdmUoaGVyZSgiZmlndXJlcy9zMS5wbmciKSwgaGVpZ2h0PTEwLCB3aWR0aCA9IDEyKQoKCmBgYAoKQ2FsY3VsYXRlIGFyZWFzIHBlciBnZW9ncmFwaGljYWwgdW5pdAoKYGBge3J9CnNmX3VzZV9zMihGQUxTRSkgI2h0dHBzOi8vZ2l0aHViLmNvbS9yLXNwYXRpYWwvc2YvaXNzdWVzLzE3NjIKCmdsYXNnb3dfd2FyZHNfMTk1MSA8LSBnbGFzZ293X3dhcmRzXzE5NTEgJT4lCiAgbXV0YXRlKGFyZWEgPSBzdF9hcmVhKGdsYXNnb3dfd2FyZHNfMTk1MSkpCgoKZ2xhc2dvd193YXJkc18xOTUxJGFyZWFfa20gPC0gdW5pdHM6OnNldF91bml0cyhnbGFzZ293X3dhcmRzXzE5NTEkYXJlYSwga21eMikKCgpgYGAKCgpNYWtlIGRpdmlzaW9uIHNoYXBlIGZpbGVzLCBhbmQgY2FsY3VsYXRlIGFyZWEKKHN0b3BwZWQgd29ya2luZywgbmVlZCB0byBmaXghKQoKYGBge3J9CgojIGdsYXNnb3dfZGl2aXNpb25zXzE5NTEgPC0gZ2xhc2dvd193YXJkc18xOTUxICU+JQojICAgZ3JvdXBfYnkoZGl2aXNpb24pICU+JSAKIyAgIHN1bW1hcml6ZShnZW9tZXRyeSA9IHN0X3VuaW9uKGdlb21ldHJ5KSkgJT4lCiMgICBubmdlbzo6c3RfcmVtb3ZlX2hvbGVzKCkgJT4lCiMgICBtdXRhdGUoYXJlYSA9IHN0X2FyZWEoZ2xhc2dvd19kaXZpc2lvbnNfMTk1MSkpCiMgCiMgZ2xhc2dvd19kaXZpc2lvbnNfMTk1MSRhcmVhX2ttIDwtIHVuaXRzOjpzZXRfdW5pdHMoZ2xhc2dvd19kaXZpc2lvbnNfMTk1MSRhcmVhLCBrbV4yKQoKCmBgYAoKCiMjIyAzLiBEZW5vbWluYXRvcnMKCkxvYWQgaW4gdGhlIGRhdGFzZXRzIGZvciBkZW5vbm9taWF0b3JzLCBhbmQgY2hlY2sgZm9yIGNvbnNpc3RlbmN5LgoKYGBge3J9CgpvdmVyYWxsX3BvcHMgPC0gcmVhZF94bHN4KHBhdGggPSAiMjAyMy0xMS0yOF9nbGFzZ293LWFjZi54bHN4Iiwgc2hlZXQgPSAib3ZlcmFsbF9wb3B1bGF0aW9uIikKCm92ZXJhbGxfcG9wcyAlPiUKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpICYgISh5ZWFyKSwgIH5jb21tYSguKSkpICU+JQogIGRhdGF0YWJsZSgpCgojc2hpZnQgeWVhciB0byBtaWRwb2ludApvdmVyYWxsX3BvcHMgPC0gb3ZlcmFsbF9wb3BzICU+JQogIG11dGF0ZSh5ZWFyMiA9IHllYXIrMC41KQoKYGBgCgpOb3RlLCB3ZSBoYXZlIHRocmVlIHBvcHVsYXRpb24gZXN0aW1hdGVzOgoKMS4gUG9wdWxhdGlvbiB3aXRob3V0IGluc3RpdHV0aW9uYWxpc2VkIHBlb3BsZSBvciBwZW9wbGUgaW4gc2hpcHBpbmcKMi4gUG9wdWxhdGlvbiBpbiBpbnN0aXR1dGlvbnMKMy4gUG9wdWxhdGlvbiBpbiBzaGlwcGluZwoKKFBvcHVsYXRpb24gaW4gc2hpcHBpbmcgaXMgZXN0aW1hdGVkIGZyb20gdGhlIDE5NTEgY2Vuc3VzLCBzbyBpcyB0aGUgc2FtZSBmb3IgbW9zdCB5ZWFycykKCiMjIyMgMy4xIE92ZXJhbGwgcG9wdWxhdGlvbgoKRmlyc3QsIHBsb3QgdGhlIHRvdGFsIHBvcHVsYXRpb24KCmBgYHtyfQoKb3ZlcmFsbF9wb3BzICU+JQogIGdncGxvdCgpICsKICBnZW9tX2FyZWEoYWVzKHk9dG90YWxfcG9wdWxhdGlvbiwgeD15ZWFyMiksIGFscGhhPTAuNSwgY29sb3VyID0gIm1lZGl1bXNlYWdyZWVuIiwgZmlsbD0ibWVkaXVtc2VhZ3JlZW4iKSArCiAgZ2VvbV9wb2ludChhZXMoeT10b3RhbF9wb3B1bGF0aW9uLCB4PXllYXIyKSwgY29sb3VyID0gIm1lZGl1bXNlYWdyZWVuIikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX3N0YXJ0KSwgbGluZXR5cGU9MykgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX2VuZCksIGxpbmV0eXBlPTMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSB5ZWFyX2xhYmVscykgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJHbGFzZ293IENvcnBvcmF0aW9uOiB0b3RhbCBwb3B1bGF0aW9uIiwKICAgIHN1YnRpdGxlID0gIjE5NTAgdG8gMTk2MyIsCiAgICB4ID0gIlllYXIiLAogICAgeSA9ICJQb3B1bGF0aW9uIiwKICAgIGNhcHRpb24gPSAiTWlkLXllYXIgZXN0aW1hdGVzXG5NYXNzIG1pbmlhdHVyZSBYLXJheSBjYW1wYWlnbiBwZXJpb2QgYmV0d2VlbiBkYXNoZWQgbGluZXMgKDExdGggTWFyY2gtMTJ0aCBBcHJpbCAxOTU3KSIKICApICsKICB0aGVtZV9nZ2Rpc3QoKQoKCmBgYAoKTm93IHRoZSBwb3B1bGF0aW9uIGV4Y2x1ZGluZyBpbnN0aXR1dGlvbmFsaXNlZCBhbmQgc2hpcHBpbmcgcG9wdWxhdGlvbgoKYGBge3J9CgpvdmVyYWxsX3BvcHMgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fYXJlYShhZXMoeT1wb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwLCB4PXllYXIyKSwgYWxwaGE9MC41LCBjb2xvdXIgPSAicHVycGxlIiwgZmlsbD0icHVycGxlIikgKwogIGdlb21fcG9pbnQoYWVzKHk9cG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgeD15ZWFyMiksIGNvbG91ciA9ICJwdXJwbGUiKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2Zfc3RhcnQpLCBsaW5ldHlwZT0zKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2ZfZW5kKSwgbGluZXR5cGU9MykgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9Y29tbWEpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHllYXJfbGFiZWxzKSArCiAgbGFicygKICAgIHRpdGxlID0gIkdsYXNnb3cgQ29ycG9yYXRpb246IHBvcHVsYXRpb24gZXhjbHVkaW5nIGluc3RpdHV0aW9uYWxpc2VkIGFuZCBzaGlwcGluZyIsCiAgICBzdWJ0aXRsZSA9ICIxOTUwIHRvIDE5NjMiLAogICAgeCA9ICJZZWFyIiwKICAgIHkgPSAiUG9wdWxhdGlvbiIsCiAgICBjYXB0aW9uID0gIk1pZC15ZWFyIGVzdGltYXRlc1xuTWFzcyBtaW5pYXR1cmUgWC1yYXkgY2FtcGFpZ24gcGVyaW9kIGJldHdlZW4gZGFzaGVkIGxpbmVzICgxMXRoIE1hcmNoLTEydGggQXByaWwgMTk1NykiCiAgKSArCiAgdGhlbWVfZ2dkaXN0KCkKCgpgYGAKCiMjIyMgMy4yIFBvcHVsYXRpb24gYnkgV2FyZAoKVGhlcmUgYXJlIDUgRGl2aXNpb25zIGNvbnRhaW5pbmcgMzcgV2FyZHMgaW4gdGhlIEdsYXNnb3cgQ29ycG9yYXRpb24sIHdpdGggY29uc2lzdGVudCBib3VuZGFyaWVzIG92ZXIgdGltZS4KCmBgYHtyfQojbG9vay11cCB0YWJsZSBmb3IgZGl2aXNpb25zIGFuZCB3YXJkcwp3YXJkX2xvb2t1cCA8LSByZWFkX3hsc3gocGF0aCA9ICIyMDIzLTExLTI4X2dsYXNnb3ctYWNmLnhsc3giLCBzaGVldCA9ICJkaXZpc2lvbnNfd2FyZHMiKQoKCndhcmRfcG9wcyA8LSByZWFkX3hsc3gocGF0aCA9ICIyMDIzLTExLTI4X2dsYXNnb3ctYWNmLnhsc3giLCBzaGVldCA9ICJ3YXJkX3BvcHVsYXRpb24iKQoKd2FyZF9wb3BzICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykgJiAhKHllYXIpLCAgfmNvbW1hKC4pKSkgJT4lCiAgZGF0YXRhYmxlKCkKCiNzaGlmdCB5ZWFyIHRvIG1pZHBvaW50CndhcmRfcG9wcyA8LSB3YXJkX3BvcHMgJT4lCiAgbXV0YXRlKHllYXIyID0geWVhciswLjUpCgojR2V0IHRoZSBEaXZpc2lvbiBwb3B1bGF0aW9uCmRpdmlzaW9uX3BvcHMgPC0gd2FyZF9wb3BzICU+JQogIGdyb3VwX2J5KGRpdmlzaW9uLCB5ZWFyKSAlPiUKICBzdW1tYXJpc2UocG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCA9IHN1bShwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBpbnN0aXR1dGlvbnMgPSBzdW0oaW5zdGl0dXRpb25zLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBzaGlwcGluZyA9IHN1bShzaGlwcGluZywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgdG90YWxfcG9wdWxhdGlvbiA9IHN1bSh0b3RhbF9wb3B1bGF0aW9uLCBuYS5ybSA9IFRSVUUpKQoKZGl2aXNpb25fcG9wcyAlPiUKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpICYgISh5ZWFyKSwgIH5jb21tYSguKSkpICU+JQogIGRhdGF0YWJsZSgpCgpgYGAKClBsb3QgdGhlIG92ZXJhbGwgcG9wdWxhdGlvbiBieSBEaXZpc2lvbiBhbmQgV2FyZAoKYGBge3J9CgpkaXZpc2lvbl9wb3BzICU+JQogIG11dGF0ZSh5ZWFyMiA9IHllYXIrMC41KSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9hcmVhKGFlcyh5PXRvdGFsX3BvcHVsYXRpb24sIHg9eWVhcjIsIGNvbG91cj1kaXZpc2lvbiwgZmlsbD1kaXZpc2lvbiksIGFscGhhPTAuOCkgKwogIGdlb21fcG9pbnQoYWVzKHk9dG90YWxfcG9wdWxhdGlvbiwgeD15ZWFyMiwgY29sb3VyPWRpdmlzaW9uKSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX3N0YXJ0KSwgbGluZXR5cGU9MykgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX2VuZCksIGxpbmV0eXBlPTMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hKSArCiAgZmFjZXRfd3JhcChkaXZpc2lvbn4uKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSB5ZWFyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9heGlzKGFuZ2xlID0gOTApKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQzIiwgbmFtZSA9ICIiKSArCiAgc2NhbGVfY29sb3VyX2JyZXdlcihwYWxldHRlID0gIlNldDMiLCBuYW1lID0gIiIpICsKICBsYWJzKAogICAgdGl0bGUgPSAiR2xhc2dvdyBDb3Jwb3JhdGlvbjogdG90YWwgcG9wdWxhdGlvbiBieSBEaXZpc2lvbiIsCiAgICBzdWJ0aXRsZSA9ICIxOTUwIHRvIDE5NjMiLAogICAgeCA9ICJZZWFyIiwKICAgIHkgPSAiUG9wdWxhdGlvbiIsCiAgICBjYXB0aW9uID0gIk1pZC15ZWFyIGVzdGltYXRlc1xuTWFzcyBtaW5pYXR1cmUgWC1yYXkgY2FtcGFpZ24gcGVyaW9kIGJldHdlZW4gZGFzaGVkIGxpbmVzICgxMXRoIE1hcmNoLTEydGggQXByaWwgMTk1NykiCiAgKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKCmBgYAoKYGBge3J9Cgp3YXJkX3BvcHMgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fYXJlYShhZXMoeT10b3RhbF9wb3B1bGF0aW9uLCB4PXllYXIyLCBjb2xvdXI9ZGl2aXNpb24sIGZpbGw9ZGl2aXNpb24pLCBhbHBoYT0wLjgpICsKICBnZW9tX3BvaW50KGFlcyh5PXRvdGFsX3BvcHVsYXRpb24sIHg9eWVhcjIsIGNvbG91cj1kaXZpc2lvbikpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9zdGFydCksIGxpbmV0eXBlPTMpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9lbmQpLCBsaW5ldHlwZT0zKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1jb21tYSkgKwogIGZhY2V0X3dyYXAod2FyZH4uLCBuY29sPTYpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2F4aXMoYW5nbGUgPSA5MCkpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDMiLCBuYW1lPSJEaXZpc2lvbiIpICsKICBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGUgPSAiU2V0MyIsIG5hbWUgPSAiRGl2aXNpb24iKSArCiAgbGFicygKICAgIHRpdGxlID0gIkdsYXNnb3cgQ2l0eTogdG90YWwgcG9wdWxhdGlvbiBieSBXYXJkIiwKICAgIHN1YnRpdGxlID0gIjE5NTAgdG8gMTk2MyIsCiAgICB4ID0gIlllYXIiLAogICAgeSA9ICJQb3B1bGF0aW9uIiwKICAgIGNhcHRpb24gPSAiTWlkLXllYXIgZXN0aW1hdGVzXG5NYXNzIG1pbmlhdHVyZSBYLXJheSBjYW1wYWlnbiBwZXJpb2QgYmV0d2VlbiBkYXNoZWQgbGluZXMgKDExdGggTWFyY2gtMTJ0aCBBcHJpbCAxOTU3KSIKICApICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCgpnZ3NhdmUoaGVyZSgiZmlndXJlcy9zMi5wbmciKSwgaGVpZ2h0PTEwLCB3aWR0aD0xMikKCmBgYAoKQXBwcm94aW1hdGVseSwgaG93IG1hbnkgcGVyc29uLXllYXJzIG9mIGZvbGxvdy11cCBkbyB3ZSBoYXZlPwoKYGBge3J9CgpvdmVyYWxsX3BvcHMgJT4lCiAgdW5ncm91cCgpICU+JQogIHN1bW1hcmlzZShhY3Jvc3MoeWVhciwgbGVuZ3RoLCAubmFtZXMgPSAieWVhcnMiKSwKICAgICAgICAgICAgYWNyb3NzKGMocG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgdG90YWxfcG9wdWxhdGlvbiksIHN1bSkpICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMuZG91YmxlKSwgY29tbWEpKSAlPiUKICBkYXRhdGFibGUoKQoKCmBgYAoKQ2hhbmdlIGluIHBvcHVsYXRpb24gYnkgd2FyZAoKYGBge3J9Cgp3YXJkX3BvcHMgJT4lCiAgZ3JvdXBfYnkod2FyZCkgJT4lCiAgc3VtbWFyaXNlKHBjdF9jaGFuZ2VfcG9wID0gKGxhc3QocG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCkgLSBmaXJzdChwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwKSkvZmlyc3QocG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCkpICU+JQogIG11dGF0ZShwY3RfY2hhbmdlX3BvcCA9IHBlcmNlbnQocGN0X2NoYW5nZV9wb3ApKSAlPiUKICBhcnJhbmdlKHBjdF9jaGFuZ2VfcG9wKSAlPiUKICBkYXRhdGFibGUoKQogIAoKCmBgYAoKT3V0cHV0IHBvcHVsYXRpb24gZGVuc2l0eSBieSB3YXJkIGFuZCBkaXZpc29uIGZvciByZWdyZXNzaW9uIG1vZGVsbGluZwoKV2FyZHMgZmlyc3QKCihzdG9wcGVkIHdvcmtpbmcsIG5lZWQgdG8gZml4KQoKYGBge3J9CgojIHdhcmRfY292YXJpYXRlcyA8LSAgZ2xhc2dvd193YXJkc18xOTUxICU+JQojICAgbGVmdF9qb2luKHdhcmRfcG9wcykgJT4lCiMgICBtdXRhdGUocGVvcGxlX3Blcl9rbV9zcSA9IGFzLmRvdWJsZShwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwL2FyZWFfa20pKQojIAojICNwbG90IGl0IG91dAojIAojIHdhcmRfY292YXJpYXRlcyAlPiUKIyAgIGdncGxvdCgpICsKIyAgIGdlb21fc2YoYWVzKGZpbGw9cGVvcGxlX3Blcl9rbV9zcSkpICsgCiMgICBmYWNldF93cmFwKHllYXJ+LiwgbmNvbD03KSArCiMgICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhvcHRpb249IkEiKSArCiMgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKIyAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0PTEpKQojIAojIGdnc2F2ZShoZXJlKCJmaWd1cmVzL3dhcmRfcG9wX2RlbnNpdHkucG5nIiksIHdpZHRoPTEwKQojIAojIHdyaXRlX3Jkcyh3YXJkX2NvdmFyaWF0ZXMsIGhlcmUoInBvcHVsYXRpb25zL3dhcmRfY292YXJpYXRlcy5yZHMiKSkKCgpgYGAKCk5vdyBkaXZpc2lvbnMgZmlyc3QKCgooc3RvcHBlZCB3b3JraW5nLCBuZWVkIHRvIGZpeCkKCmBgYHtyfQoKIyBkaXZpc2lvbl9jb3ZhcmlhdGVzIDwtICBnbGFzZ293X2RpdmlzaW9uc18xOTUxICU+JQojICAgbGVmdF9qb2luKGRpdmlzaW9uX3BvcHMpICU+JQojICAgbXV0YXRlKHBlb3BsZV9wZXJfa21fc3EgPSBhcy5kb3VibGUodG90YWxfcG9wdWxhdGlvbi9hcmVhX2ttKSkKIyAKIyAjcGxvdCBpdCBvdXQKIyAKIyBkaXZpc2lvbl9jb3ZhcmlhdGVzICU+JQojICAgZ2dwbG90KCkgKwojICAgZ2VvbV9zZihhZXMoZmlsbD1wZW9wbGVfcGVyX2ttX3NxKSkgKyAKIyAgIGdlb21fc2ZfbGFiZWwoYWVzKGxhYmVsID0gZGl2aXNpb24pLCBzaXplPTMsIGZpbGw9TkEsIGxhYmVsLnNpemUgPSBOQSwgY29sb3VyPSJibGFjayIsIGZhbWlseSA9ICJTZWdvZSBVSSIpICsKIyAgIGZhY2V0X3dyYXAoeWVhcn4uLCBuY29sPTcpICsKIyAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbj0iRyIpICsKIyAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAojICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3Q9MSkpCiMgCiMgZ2dzYXZlKGhlcmUoImZpZ3VyZXMvZGl2aXNpb25fcG9wX2RlbnNpdHkucG5nIiksIHdpZHRoPTEwKQojIAojIHdyaXRlX3JkcyhkaXZpc2lvbl9jb3ZhcmlhdGVzLCBoZXJlKCJwb3B1bGF0aW9ucy9kaXZpc2lvbl9jb3ZhcmlhdGVzLnJkcyIpKQoKYGBgCgoKIyMjIyAzLjMgUG9wdWxhdGlvbiBieSBhZ2UgYW5kIHNleAoKYGBge3J9CgphZ2Vfc2V4IDwtIHJlYWRfeGxzeChwYXRoID0gIjIwMjMtMTEtMjhfZ2xhc2dvdy1hY2YueGxzeCIsIHNoZWV0ID0gImFnZV9zZXhfcG9wdWxhdGlvbiIpICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gYyhtYWxlLCBmZW1hbGUpLAogICAgICAgICAgICAgICBuYW1lc190byA9ICJzZXgiKQoKI2NvbGxhcHNlIGRvd24gdG8gc21hbGxlciBhZ2UgZ3JvdXBzIHRvIGJlIG1hbmFnZWFibGUKYWdlX3NleCA8LSBhZ2Vfc2V4ICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUoYWdlID0gY2FzZV93aGVuKGFnZSA9PSAiMCB0byA0IiB+ICIwMCB0byAwNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2UgPT0gIjUgdG8gOSIgfiAiMDUgdG8gMTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICIxMCB0byAxNCIgfiAiMDUgdG8gMTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICIxNSB0byAxOSIgfiAiMTUgdG8gMjQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICIyMCB0byAyNCIgfiAiMTUgdG8gMjQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICIyNSB0byAyOSIgfiAiMjUgdG8gMzQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICIzMCB0byAzNCIgfiAiMjUgdG8gMzQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICIzNSB0byAzOSIgfiAiMzUgdG8gNDQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICI0MCB0byA0NCIgfiAiMzUgdG8gNDQiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICI0NSB0byA0OSIgfiAiNDUgdG8gNTkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICI1MCB0byA1NCIgfiAiNDUgdG8gNTkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlID09ICI1NSB0byA1OSIgfiAiNDUgdG8gNTkiLAogICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICI2MCAmIHVwIikpICU+JQogIGdyb3VwX2J5KHllYXIsIGFnZSwgc2V4KSAlPiUKICBtdXRhdGUodmFsdWUgPSBzdW0odmFsdWUpKSAlPiUKICB1bmdyb3VwKCkKCgoKbV9hZ2Vfc2V4IDwtIGxtKHZhbHVlIH4gc3BsaW5lczo6bnMoeWVhciwga25vdHMgPSAzKSphZ2Uqc2V4LCBkYXRhID0gYWdlX3NleCkKCnN1bW1hcnkobV9hZ2Vfc2V4KQoKYWdlX2xldmVscyA8LSBhZ2Vfc2V4ICU+JSBzZWxlY3QoYWdlKSAlPiUgZGlzdGluY3QoKSAlPiUgcHVsbCgpIAoKYWdlX3NleF9uZCA8LSAKICBjcm9zc2luZygKICAgIGFnZT1hZ2VfbGV2ZWxzLAogICAgc2V4PWMoIm1hbGUiLCAiZmVtYWxlIiksCiAgICB5ZWFyID0gMTk1MDoxOTYzCiAgKQoKcHJlZF9wb3BzIDwtIGFnZV9zZXhfbmQgJT4lIG1vZGVscjo6YWRkX3ByZWRpY3Rpb25zKG1fYWdlX3NleCkKCnByZWRfcG9wcyAlPiUKICBnZ3Bsb3QoYWVzKHg9eWVhciwgeT1wcmVkLCBjb2xvdXI9YWdlKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KCkgKwogIGZhY2V0X2dyaWQoc2V4fi4pICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEsIGxpbWl0cyA9IGMoMCwgMTI1MDAwKSkKCiNIb3cgd2VsbCBkbyB0aGV5IG1hdGNoIHVwIHdpdGggb3VyIG92ZXJhbGwgcG9wdWxhdGlvbnM/CnByZWRfcG9wcyAlPiUKICBncm91cF9ieSh5ZWFyKSAlPiUKICBzdW1tYXJpc2Uoc3VtX3ByZWRfcG9wID0gc3VtKHByZWQpKSAlPiUKICByaWdodF9qb2luKG92ZXJhbGxfcG9wcykgJT4lCiAgc2VsZWN0KHllYXIsIHN1bV9wcmVkX3BvcCwgcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgdG90YWxfcG9wdWxhdGlvbikgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKHN1bV9wcmVkX3BvcCwgcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgdG90YWxfcG9wdWxhdGlvbikpICU+JQogIGdncGxvdChhZXMoeD15ZWFyLCB5PXZhbHVlLCBjb2xvdXI9bmFtZSkpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSwgbGltaXRzID0gYyg4MDAwMDAsIDEyNTAwMDApKQoKcHJlZF9wb3BzICU+JQogIGdyb3VwX2J5KHllYXIsIHNleCkgJT4lCiAgc3VtbWFyaXNlKHN1bSA9IHN1bShwcmVkKSkgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgbXV0YXRlKHNleF9yYXRpbyA9IGZpcnN0KHN1bSkvbGFzdChzdW0pKQpgYGAKCgpXaGF0IHBlcmNlbnRhZ2Ugb2YgYWR1bHRzICgxNSsgcGFydGljaXBhdGVkIGluIHRoZSBpbnRlcnZlbnRpb24gaW4gMTk1Nyk/CgpgYGB7cn0KCnByZWRfcG9wcyAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZmlsdGVyKHllYXI9PTE5NTcpICU+JQogIGZpbHRlcihhZ2UgIT0gIjAwIHRvIDA0IiwKICAgICAgICAgYWdlICE9ICIwNSB0byAxNCIpICU+JQogIHN1bW1hcmlzZSh0b3RhbF9wb3AgPSBzdW0ocHJlZCkpICU+JQogIG11dGF0ZShjeHJfc2NyZWVuZWQgPSA2MjIzNDkpICU+JQogIG11dGF0ZShwY3RfcG9wX2N4cl9zY3JlZW5lZCA9IHBlcmNlbnQoY3hyX3NjcmVlbmVkL3RvdGFsX3BvcCkpCgpwcmVkX3BvcHMgJT4lCiAgdW5ncm91cCgpICU+JQogIGZpbHRlcih5ZWFyPT0xOTU3KSAlPiUKICBmaWx0ZXIoYWdlICE9ICIwMCB0byAwNCIsCiAgICAgICAgIGFnZSAhPSAiMDUgdG8gMTQiKSAlPiUKICBzdW1tYXJpc2UodG90YWxfcG9wID0gc3VtKHByZWQpLCAuYnk9c2V4KSAlPiUKICBtdXRhdGUoY3hyX3NjcmVlbmVkID0gYygzNDA0NzQsIDI4MTg3NSkpICU+JQogIG11dGF0ZShwY3RfcG9wX2N4cl9zY3JlZW5lZCA9IHBlcmNlbnQoY3hyX3NjcmVlbmVkL3RvdGFsX3BvcCkpCgoKYGBgCgoKUG9wdWxhdGlvbiBweXJhbWlkcwoKYGBge3J9CgpsYWJlbF9hYnMgPC0gZnVuY3Rpb24oeCkgewogIGNvbW1hKGFicyh4KSkKfQoKCnByZWRfcG9wcyAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgbXV0YXRlKHllYXJfcG9wID0gc3VtKHByZWQpLAogICAgICAgICBhZ2Vfc2V4X3BjdCA9IHBlcmNlbnQocHJlZC95ZWFyX3BvcCwgYWNjdXJhY3k9MC4xKSkgJT4lCiAgbXV0YXRlKHNleCA9IGNhc2Vfd2hlbihzZXg9PSJtYWxlIiB+ICJNYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgIHNleD09ImZlbWFsZSIgfiAiRmVtYWxlIikpICU+JQogIGdncGxvdCgKICAgIGFlcyh4ID0gYWdlLCBmaWxsID0gc2V4LCAKICAgICAgICB5ID0gaWZlbHNlKHRlc3QgPSBzZXggPT0gIkZlbWFsZSIseWVzID0gLXByZWQsIG5vID0gcHJlZCkpKSArIAogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGFnZV9zZXhfcGN0KSwKICAgICAgICAgICAgcG9zaXRpb249IHBvc2l0aW9uX3N0YWNrKHZqdXN0PTAuNSksIGNvbG91cj0id2hpdGUiLCBzaXplPTIuNSkgKwogIGZhY2V0X3dyYXAoeWVhcn4uLCBuY29sPTcpICsKICBjb29yZF9mbGlwKCkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBsYWJlbF9hYnMpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJtZWRpdW1zZWFncmVlbiIsICJwdXJwbGUiKSwgbmFtZT0iIikgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCwgaGp1c3QgPSAxLCB2anVzdD0wLjUpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSkpICsKICBsYWJzKHg9IiIsIHk9IiIpIAoKCmdnc2F2ZShoZXJlKCJmaWd1cmVzL3MzLnBuZyIpLCB3aWR0aD0xMCkKCgpgYGAKCk5vdCBwZXJmZWN0LCBidXQgcmVzb25hYmx5IGdvb2QuIEJ1dCBhaGhoaGguLi4gdGhlIGFnZSBncm91cHMgZG9uJ3QgYWxpZ24gd2l0aCB0aGUgY2FzZSBub3RpZmljYXRpb24gYWdlIGdyb3VwcyEgQ29tZSBiYWNrIHRvIHRoaW5rIGFib3V0IHRoaXMgbGF0ZXIuCgoKIyMjIDQuIFR1YmVyY3Vsb3NpcyBjYXNlcwoKSW1wb3J0IHRoZSB0dWJlcmN1bG9zaXMgY2FzZXMgZGF0YXNldAoKCiMjIyMgNC4xIE92ZXJhbGwgbm90aWZpY2F0aW9ucwoKT3ZlcmFsbCwgYnkgeWVhci4KCmBgYHtyfQoKY2FzZXNfYnlfeWVhciA8LSByZWFkX3hsc3goIjIwMjMtMTEtMjhfZ2xhc2dvdy1hY2YueGxzeCIsIHNoZWV0ID0gImJ5X3llYXIiKQoKY2FzZXNfYnlfeWVhciU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykgJiAhKHllYXIpLCAgfmNvbW1hKC4pKSkgJT4lCiAgZGF0YXRhYmxlKCkKCgojc2hpZnQgeWVhciB0byBtaWRwb2ludApjYXNlc19ieV95ZWFyIDwtIGNhc2VzX2J5X3llYXIgJT4lCiAgbXV0YXRlKHllYXIyID0geWVhciswLjUpCgpgYGAKClBsb3QgdGhlIG92ZXJhbGwgbnVtYmVyIG9mIGNhc2Ugbm90aWZpZWQgcGVyIHllYXIsIGJ5IHB1bG1vbmFyeSBhbmQgZXh0cmEgcHVsbW9uYXJ5IGNsYXNzaWZpY2F0aW9uLgoKYGBge3J9CgpjYXNlc19ieV95ZWFyICU+JQogIHNlbGVjdCgtdG90YWxfbm90aWZpY2F0aW9ucywgLXllYXIpICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gYyhwdWxtb25hcnlfbm90aWZpY2F0aW9ucywgYG5vbi1wdWxtb25hcnlfbm90aWZpY2F0aW9uc2ApKSAlPiUKICBtdXRhdGUobmFtZSA9IGNhc2Vfd2hlbihuYW1lID09ICJwdWxtb25hcnlfbm90aWZpY2F0aW9ucyIgfiAiUHVsbW9uYXJ5IFRCIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID09ICJub24tcHVsbW9uYXJ5X25vdGlmaWNhdGlvbnMiIH4gIkV4dHJhLXB1bG1vbmFyeSBUQiIpKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9hcmVhKGFlcyh5PXZhbHVlLCB4PXllYXIyLCBncm91cCA9IG5hbWUsIGZpbGw9bmFtZSksIGFscGhhPTAuNSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX3N0YXJ0KSwgbGluZXR5cGU9MykgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX2VuZCksIGxpbmV0eXBlPTMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSB5ZWFyX2xhYmVscykgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIsIG5hbWU9IiIpICsKICBsYWJzKAogICAgdGl0bGUgPSAiR2xhc2dvdyBDb3Jwb3JhdGlvbjogVHViZXJjdWxvc2lzIG5vdGlmaWNhdGlvbnMiLAogICAgc3VidGl0bGUgPSAiMTk1MCB0byAxOTYzLCBieSBUQiBkaXNlYXNlIGNsYXNzaWZpY2F0aW9uIiwKICAgIHggPSAiWWVhciIsCiAgICB5ID0gIk51bWJlciBvZiBjYXNlcyIsCiAgICBjYXB0aW9uID0gIk1hc3MgbWluaWF0dXJlIFgtcmF5IGNhbXBhaWduIHBlcmlvZCBiZXR3ZWVuIGRhc2hlZCBsaW5lcyAoMTF0aCBNYXJjaC0xMnRoIEFwcmlsIDE5NTcpIgogICkgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKICAKCmBgYAoKIyMjIyA0LjIgTm90aWZpY2F0aW9ucyBieSBEaXZpc2lvbgoKUmVhZCBpbiB0aGUgZGF0YXNldHMgYW5kIG1lcmdlIHRvZ2V0aGVyLgoKYGBge3J9CgojbGlzdCBhbGwgdGhlIHNoZWV0cwphbGxfc2hlZXRzIDwtIGV4Y2VsX3NoZWV0cygiMjAyMy0xMS0yOF9nbGFzZ293LWFjZi54bHN4IikKCiNnZXQgdGhlIHdhcmQgc2hlZXRzCndhcmRfc2hlZXRzIDwtIGVuZnJhbWUoYWxsX3NoZWV0cykgJT4lCiAgZmlsdGVyKGdyZXBsKCJieV93YXJkIiwgdmFsdWUpKSAlPiUKICBwdWxsKHZhbHVlKQoKCmNhc2VzX2J5X3dhcmRfc2V4X3llYXIgPC0gbWFwX2RmKHdhcmRfc2hlZXRzLCB+cmVhZF94bHN4KHBhdGggPSAiMjAyMy0xMS0yOF9nbGFzZ293LWFjZi54bHN4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaGVldCA9IC4pKQoKY2FzZXNfYnlfd2FyZF9zZXhfeWVhciAlPiUKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpICYgISh5ZWFyKSwgIH5jb21tYSguKSkpICU+JQogIGRhdGF0YWJsZSgpCgpgYGAKCkFnZ3JlZ2F0ZSB0b2dldGhlciB0byBnZXQgY2FzZXMgYnkgZGl2aXNpb24KCmBgYHtyfQoKY2FzZXNfYnlfZGl2aXNpb24gPC0gY2FzZXNfYnlfd2FyZF9zZXhfeWVhciAlPiUKICBsZWZ0X2pvaW4od2FyZF9sb29rdXApICU+JQogIGdyb3VwX2J5KGRpdmlzaW9uLCB5ZWFyLCB0Yl90eXBlKSAlPiUKICBzdW1tYXJpc2UoY2FzZXMgPSBzdW0oY2FzZXMsIG5hLnJtID0gVFJVRSkpCgojc2hpZnQgeWVhciB0byBtaWRwb2ludApjYXNlc19ieV9kaXZpc2lvbiA8LSBjYXNlc19ieV9kaXZpc2lvbiAlPiUKICBtdXRhdGUoeWVhcjIgPSB5ZWFyKzAuNSkgJT4lCiAgdW5ncm91cCgpCgpjYXNlc19ieV9kaXZpc2lvbiAgJT4lCiAgc2VsZWN0KC15ZWFyMikgJT4lCiAgc2VsZWN0KHllYXIsIGV2ZXJ5dGhpbmcoKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSAmICEoeWVhciksICB+Y29tbWEoLikpKSAlPiUKICBkYXRhdGFibGUoKQoKCmNhc2VzX2J5X2RpdmlzaW9uICU+JQogIG11dGF0ZSh0Yl90eXBlID0gY2FzZV93aGVuKHRiX3R5cGUgPT0gIlB1bG1vbmFyeSIgfiAiUHVsbW9uYXJ5IFRCIiwKICAgICAgICAgICAgICAgICAgICAgICAgICB0Yl90eXBlID09ICJOb24tUHVsbW9uYXJ5IiB+ICJFeHRyYS1wdWxtb25hcnkgVEIiKSkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fYXJlYShhZXMoeT1jYXNlcywgeD15ZWFyMiwgZ3JvdXAgPSB0Yl90eXBlLCBmaWxsPXRiX3R5cGUpLCBhbHBoYT0wLjUpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9zdGFydCksIGxpbmV0eXBlPTMpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9lbmQpLCBsaW5ldHlwZT0zKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1jb21tYSkgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSB5ZWFyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfYXhpcyhhbmdsZSA9IDkwKSkgKwogIGZhY2V0X3dyYXAoZGl2aXNpb25+Liwgc2NhbGVzID0gImZyZWVfeSIpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDEiLCBuYW1lPSIiKSArCiAgbGFicygKICAgIHRpdGxlID0gIkdsYXNnb3cgQ29ycG9yYXRpb246IFR1YmVyY3Vsb3NpcyBub3RpZmljYXRpb25zIGJ5IERpdmlzaW9uIiwKICAgIHN1YnRpdGxlID0gIjE5NTAgdG8gMTk2MywgYnkgVEIgZGlzZWFzZSBjbGFzc2lmaWNhdGlvbiIsCiAgICB4ID0gIlllYXIiLAogICAgeSA9ICJOdW1iZXIgb2YgY2FzZXMiLAogICAgY2FwdGlvbiA9ICJNYXNzIG1pbmlhdHVyZSBYLXJheSBjYW1wYWlnbiBwZXJpb2QgYmV0d2VlbiBkYXNoZWQgbGluZXMgKDExdGggTWFyY2gtMTJ0aCBBcHJpbCAxOTU3KVxuTm90ZTogZXh0cmEtcHVsbW9uYXJ5IFRCIGNhc2VzIGJ5IERpdmlzaW9uL1dhcmQgbm90IHJlcG9ydGVkIGluIDE5NjItMTk2MyIKICApICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCgpgYGAKCiMjIyMgNC4zIE5vdGlmaWNhdGlvbnMgYnkgd2FyZAoKYGBge3J9CgoKY2FzZXNfYnlfd2FyZCA8LSBjYXNlc19ieV93YXJkX3NleF95ZWFyICU+JQogIGdyb3VwX2J5KHdhcmQsIHllYXIsIHRiX3R5cGUpICU+JQogIHN1bW1hcmlzZShjYXNlcyA9IHN1bShjYXNlcywgbmEucm0gPSBUUlVFKSkgJT4lCiAgdW5ncm91cCgpCgpjYXNlc19ieV93YXJkICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykgJiAhKHllYXIpLCAgfmNvbW1hKC4pKSkgJT4lCiAgc2VsZWN0KHllYXIsIGV2ZXJ5dGhpbmcoKSkgJT4lCiAgZGF0YXRhYmxlKCkKCiNzaGlmdCB5ZWFyIHRvIG1pZHBvaW50CmNhc2VzX2J5X3dhcmQgPC0gY2FzZXNfYnlfd2FyZCAlPiUKICBtdXRhdGUoeWVhcjIgPSB5ZWFyKzAuNSkKCmNhc2VzX2J5X3dhcmQgJT4lCiAgbXV0YXRlKHRiX3R5cGUgPSBjYXNlX3doZW4odGJfdHlwZSA9PSAiUHVsbW9uYXJ5IiB+ICJQdWxtb25hcnkgVEIiLAogICAgICAgICAgICAgICAgICAgICAgICAgIHRiX3R5cGUgPT0gIk5vbi1QdWxtb25hcnkiIH4gIkV4dHJhLXB1bG1vbmFyeSBUQiIpKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9hcmVhKGFlcyh5PWNhc2VzLCB4PXllYXIyLCBncm91cCA9IHRiX3R5cGUsIGZpbGw9dGJfdHlwZSksIGFscGhhPTAuOCkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX3N0YXJ0KSwgbGluZXR5cGU9MykgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX2VuZCksIGxpbmV0eXBlPTMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSB5ZWFyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9heGlzKGFuZ2xlID0gOTApKSArCiAgZmFjZXRfd3JhcCh3YXJkfi4sIHNjYWxlcyA9ICJmcmVlX3kiKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIiwgbmFtZT0iIikgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJHbGFzZ293IENvcnBvcmF0aW9uOiBUdWJlcmN1bG9zaXMgbm90aWZpY2F0aW9ucyBieSBXYXJkIiwKICAgIHN1YnRpdGxlID0gIjE5NTAgdG8gMTk2MywgYnkgVEIgZGlzZWFzZSBjbGFzc2lmaWNhdGlvbiIsCiAgICB4ID0gIlllYXIiLAogICAgeSA9ICJOdW1iZXIgb2YgY2FzZXMiLAogICAgY2FwdGlvbiA9ICJNYXNzIG1pbmlhdHVyZSBYLXJheSBjYW1wYWlnbiBwZXJpb2QgYmV0d2VlbiBkYXNoZWQgbGluZXMgKDExdGggTWFyY2gtMTJ0aCBBcHJpbCAxOTU3KVxuTm90ZTogZXh0cmEtcHVsbW9uYXJ5IFRCIGNhc2VzIGJ5IERpdmlzaW9uL1dhcmQgbm90IHJlcG9ydGVkIGluIDE5NjItMTk2MyIKICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCgpgYGAKCiMjIyMgNC40IE5vdGlmaWNhdGlvbnMgYnkgYWdlIGFuZCBzZXgKCkFzIHdlIGRvbid0IGhhdmUgZGVub21pbmF0b3JzLCB3ZSB3aWxsIGp1c3QgbW9kZWwgdGhlIGNoYW5nZSBpbiBjb3VudHMuCgpgYGB7cn0KCiNsaXN0IGFsbCB0aGUgc2hlZXRzCmFsbF9zaGVldHMgPC0gZXhjZWxfc2hlZXRzKCIyMDIzLTExLTI4X2dsYXNnb3ctYWNmLnhsc3giKQoKI2dldCB0aGUgd2FyZCBzaGVldHMKYWdlX3NleF9zaGVldHMgPC0gZW5mcmFtZShhbGxfc2hlZXRzKSAlPiUKICBmaWx0ZXIoZ3JlcGwoImJ5X2FnZV9zZXgiLCB2YWx1ZSkpICU+JQogIHB1bGwodmFsdWUpCgoKY2FzZXNfYnlfYWdlX3NleCA8LSBtYXBfZGYoYWdlX3NleF9zaGVldHMsIH5yZWFkX3hsc3gocGF0aCA9ICIyMDIzLTExLTI4X2dsYXNnb3ctYWNmLnhsc3giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoZWV0ID0gLikpCgpjYXNlc19ieV9hZ2Vfc2V4ICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykgJiAhKHllYXIpLCAgfmNvbW1hKC4pKSkgJT4lCiAgZGF0YXRhYmxlKCkKCgpgYGAKCgoKCiMjIyA1IFRCIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGVzCgojIyMjIDUuMSBPdmVyYWxsIFRCIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGVzCgpOb3cgY2FsY3VsYXRlIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGVzIHBlciAxMDAsMDAwIHBvcHVsYXRpb24KCk1lcmdlIHRoZSBub3RpZmljYXRpb24gYW5kIHBvcHVsYXRpb24gZGVub21pbmF0b3IgZGF0YXNldHMgdG9nZXRoZXIuCgpIZXJlIHdlIG5lZWQgdG8gaW5jbHVkZSB0aGUgd2hvbGUgcG9wdWxhdGlvbiAod2l0aCBzaGlwcGluZyBhbmQgaW5zdGl0dXRpb25zKSBhcyB0aGV5IGFyZSBpbmNsdWRlZCBpbiB0aGUgbm90aWZpY2F0aW9ucy4KCmBgYHtyfQoKb3ZlcmFsbF9pbmMgPC0gb3ZlcmFsbF9wb3BzICU+JQogIGxlZnRfam9pbihjYXNlc19ieV95ZWFyKQoKb3ZlcmFsbF9pbmMgPC0gb3ZlcmFsbF9pbmMgJT4lCiAgbXV0YXRlKGluY19wdWxtXzEwMGsgPSBwdWxtb25hcnlfbm90aWZpY2F0aW9ucy90b3RhbF9wb3B1bGF0aW9uKjEwMDAwMCwKICAgICAgICAgaW5jX2VwXzEwMGsgPSBgbm9uLXB1bG1vbmFyeV9ub3RpZmljYXRpb25zYC90b3RhbF9wb3B1bGF0aW9uKjEwMDAwMCwKICAgICAgICAgaW5jXzEwMGsgPSB0b3RhbF9ub3RpZmljYXRpb25zL3RvdGFsX3BvcHVsYXRpb24qMTAwMDAwKQoKb3ZlcmFsbF9pbmMgJT4lCiAgc2VsZWN0KHllYXIsIGluY18xMDBrLCBpbmNfcHVsbV8xMDBrLCBpbmNfZXBfMTAwaykgJT4lCiAgbXV0YXRlX2F0KC52YXJzID0gdmFycyhpbmNfMTAwaywgaW5jX3B1bG1fMTAwaywgaW5jX2VwXzEwMGspLAogICAgICAgICAgICAuZnVucyA9IGZ1bnMocm91bmQpKSAlPiUKICBkYXRhdGFibGUoKQoKYGBgCgpgYGB7cn0KCm92ZXJhbGxfaW5jICU+JQogIHNlbGVjdCh5ZWFyMiwgaW5jX3B1bG1fMTAwaywgaW5jX2VwXzEwMGspICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gYyhpbmNfcHVsbV8xMDBrLCBgaW5jX2VwXzEwMGtgKSkgJT4lCiAgbXV0YXRlKG5hbWUgPSBjYXNlX3doZW4obmFtZSA9PSAiaW5jX3B1bG1fMTAwayIgfiAiUHVsbW9uYXJ5IFRCIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID09ICJpbmNfZXBfMTAwayIgfiAiRXh0cmEtcHVsbW9uYXJ5IFRCIikpICU+JQogIGdncGxvdCgpICsKICBnZW9tX2FyZWEoYWVzKHk9dmFsdWUsIHg9eWVhcjIsIGdyb3VwID0gbmFtZSwgZmlsbD1uYW1lKSwgYWxwaGE9MC41KSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2Zfc3RhcnQpLCBsaW5ldHlwZT0zKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1hY2ZfZW5kKSwgbGluZXR5cGU9MykgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9Y29tbWEpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHllYXJfbGFiZWxzKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIiwgbmFtZT0iIikgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJHbGFzZ293IENvcnBvcmF0aW9uOiBUdWJlcmN1bG9zaXMgY2FzZSBub3RpZmljYXRpb24gcmF0ZSIsCiAgICBzdWJ0aXRsZSA9ICIxOTUwIHRvIDE5NjMsIGJ5IFRCIGRpc2Vhc2UgY2xhc3NpZmljYXRpb24iLAogICAgeCA9ICJZZWFyIiwKICAgIHkgPSAiQ2FzZSBub3RpZmljYXRpb24gcmF0ZSAocGVyIDEwMCwwMDApIiwKICAgIGNhcHRpb24gPSAiTWFzcyBtaW5pYXR1cmUgWC1yYXkgY2FtcGFpZ24gcGVyaW9kIGJldHdlZW4gZGFzaGVkIGxpbmVzICgxMXRoIE1hcmNoLTEydGggQXByaWwgMTk1NykiCiAgKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKCgpgYGAKCiMjIyMgNS4yIFRCIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGVzIGJ5IERpdmlzaW9uCgpgYGB7cn0KCmRpdmlzaW9uX2luYyA8LSBkaXZpc2lvbl9wb3BzICU+JQogIGxlZnRfam9pbihjYXNlc19ieV9kaXZpc2lvbikKCgpkaXZpc2lvbl9pbmMgPC0gZGl2aXNpb25faW5jICU+JQogIG11dGF0ZShpbmNfMTAwayA9IGNhc2VzL3RvdGFsX3BvcHVsYXRpb24qMTAwMDAwKQoKZGl2aXNpb25faW5jICU+JQogIHNlbGVjdCh5ZWFyLCBkaXZpc2lvbiwgdGJfdHlwZSwgaW5jXzEwMGspICU+JQogIG11dGF0ZV9hdCgudmFycyA9IHZhcnMoaW5jXzEwMGspLAogICAgICAgICAgICAuZnVucyA9IGZ1bnMocm91bmQpKSAlPiUKICBkYXRhdGFibGUoKQoKCmBgYAoKYGBge3J9CgpkaXZpc2lvbl9pbmMgJT4lCiAgbXV0YXRlKHRiX3R5cGUgPSBjYXNlX3doZW4odGJfdHlwZSA9PSAiUHVsbW9uYXJ5IiB+ICJQdWxtb25hcnkgVEIiLAogICAgICAgICAgICAgICAgICAgICAgICAgIHRiX3R5cGUgPT0gIk5vbi1QdWxtb25hcnkiIH4gIkV4dHJhLXB1bG1vbmFyeSBUQiIpKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9hcmVhKGFlcyh5PWluY18xMDBrLCB4PXllYXIyLCBncm91cCA9IHRiX3R5cGUsIGZpbGw9dGJfdHlwZSksIGFscGhhPTAuNSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX3N0YXJ0KSwgbGluZXR5cGU9MykgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX2VuZCksIGxpbmV0eXBlPTMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSB5ZWFyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9heGlzKGFuZ2xlID0gOTApKSArCiAgZmFjZXRfd3JhcChkaXZpc2lvbn4uKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIiwgbmFtZT0iIikgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJHbGFzZ293IENvcnBvcmF0aW9uOiBUdWJlcmN1bG9zaXMgY2FzZSBub3RpZmljYXRpb24gcmF0ZSwgYnkgRGl2aXNpb24iLAogICAgc3VidGl0bGUgPSAiMTk1MCB0byAxOTYzLCBieSBUQiBkaXNlYXNlIGNsYXNzaWZpY2F0aW9uIiwKICAgIHggPSAiWWVhciIsCiAgICB5ID0gIkNhc2Ugbm90aWZpY2F0aW9uIHJhdGUgKHBlciAxMDAsMDAwKSIsCiAgICBjYXB0aW9uID0gIk1hc3MgbWluaWF0dXJlIFgtcmF5IGNhbXBhaWduIHBlcmlvZCBiZXR3ZWVuIGRhc2hlZCBsaW5lcyAoMTF0aCBNYXJjaC0xMnRoIEFwcmlsIDE5NTcpXG5Ob3RlOiBleHRyYS1wdWxtb25hcnkgVEIgY2FzZXMgYnkgRGl2aXNpb24vV2FyZCBub3QgcmVwb3J0ZWQgaW4gMTk2Mi0xOTYzIgogICkgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCgoKYGBgCgojIyMjIDUuMiBUQiBjYXNlIG5vdGlmaWNhdGlvbiByYXRlcyBieSBXYXJkCgpIZXJlIHdlIHdpbGwgZmlsdGVyIG91dCB0aGUgaW5zdGl0dXRpb25zIGFuZCBoYXJib3VyIGZyb20gdGhlIGRlbm9taW5hdG9ycywgYXMgd2UgZG9uJ3QgaGF2ZSByZWxpYWJsZSBwb3B1bGF0aW9uIGRlbm9taW5hdG9ycyBmb3IgdGhlbS4KCmBgYHtyfQoKd2FyZF9pbmMgPC0gd2FyZF9wb3BzICU+JQogIGxlZnRfam9pbihjYXNlc19ieV93YXJkKQoKCndhcmRfaW5jIDwtIHdhcmRfaW5jICU+JQogIG11dGF0ZShpbmNfMTAwayA9IGNhc2VzL3BvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAqMTAwMDAwKQoKd2FyZF9pbmMgJT4lCiAgc2VsZWN0KHllYXIsIHdhcmQsIHRiX3R5cGUsIGluY18xMDBrKSAlPiUKICBtdXRhdGVfYXQoLnZhcnMgPSB2YXJzKGluY18xMDBrKSwKICAgICAgICAgICAgLmZ1bnMgPSBmdW5zKHJvdW5kKSkgJT4lCiAgZGF0YXRhYmxlKCkKCgpgYGAKCgpgYGB7cn0KCndhcmRfaW5jICU+JQogIG11dGF0ZSh0Yl90eXBlID0gY2FzZV93aGVuKHRiX3R5cGUgPT0gIlB1bG1vbmFyeSIgfiAiUHVsbW9uYXJ5IFRCIiwKICAgICAgICAgICAgICAgICAgICAgICAgICB0Yl90eXBlID09ICJOb24tUHVsbW9uYXJ5IiB+ICJFeHRyYS1wdWxtb25hcnkgVEIiKSkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fYXJlYShhZXMoeT1pbmNfMTAwaywgeD15ZWFyMiwgZ3JvdXAgPSB0Yl90eXBlLCBmaWxsPXRiX3R5cGUpLCBhbHBoYT0wLjUpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9zdGFydCksIGxpbmV0eXBlPTMpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9lbmQpLCBsaW5ldHlwZT0zKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1jb21tYSkgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSB5ZWFyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0geWVhcl9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfYXhpcyhhbmdsZSA9IDkwKSkgKwogIGZhY2V0X3dyYXAod2FyZH4uKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIiwgbmFtZT0iIikgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJHbGFzZ293IENvcnBvcmF0aW9uOiBUdWJlcmN1bG9zaXMgY2FzZSBub3RpZmljYXRpb24gcmF0ZSwgYnkgV2FyZCIsCiAgICBzdWJ0aXRsZSA9ICIxOTUwIHRvIDE5NjMsIGJ5IFRCIGRpc2Vhc2UgY2xhc3NpZmljYXRpb24iLAogICAgeCA9ICJZZWFyIiwKICAgIHkgPSAiSW5jaWRlbmNlIChwZXIgMTAwLDAwMCkiLAogICAgY2FwdGlvbiA9ICJNYXNzIG1pbmlhdHVyZSBYLXJheSBjYW1wYWlnbiBwZXJpb2QgYmV0d2VlbiBkYXNoZWQgbGluZXMgKDExdGggTWFyY2gtMTJ0aCBBcHJpbCAxOTU3KVxuTm90ZTogZXh0cmEtcHVsbW9uYXJ5IFRCIGNhc2VzIGJ5IERpdmlzaW9uL1dhcmQgbm90IHJlcG9ydGVkIGluIDE5NjItMTk2MyIKICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCgoKCmBgYAoKT24gYSBtYXAKCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQoKc3RfYXNfc2YobGVmdF9qb2luKHdhcmRfaW5jLCBnbGFzZ293X3dhcmRzXzE5NTEpKSAlPiUKICBmaWx0ZXIodGJfdHlwZT09IlB1bG1vbmFyeSIpICU+JQogIGdncGxvdCgpICsKICBnZW9tX3NmKGFlcyhmaWxsPWluY18xMDBrKSkgKwogIGZhY2V0X3dyYXAoeWVhcn4uLCBuY29sID0gNykgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG5hbWU9IkNhc2Ugbm90aWZpY2F0aW9uIHJhdGUgKHBlciAxMDAsMDAwKSIsCiAgICAgICAgICAgICAgICAgICAgICAgb3B0aW9uID0gIkEiKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICAgIGxlZ2VuZC5rZXkud2lkdGggPSB1bml0KDIsICJjbSIpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSkpICsKICBndWlkZXMoZmlsbD1ndWlkZV9jb2xvcmJhcih0aXRsZS5wb3NpdGlvbiA9ICJ0b3AiKSkKCgoKYGBgCgoKIyMjIDYuIFRCIE1vcnRhbGl0eQoKIyMjIyA2LjEgT3ZlcmFsbCBNb3J0YWxpdHkKCkltcG9ydCB0aGUgVEIgbW9ydGFsaXR5IGRhdGEuCgpGaXJzdCwgb3ZlcmFsbCBkZWF0aHMuIE5vdGUgdGhhdCBpbiB0aGUgb3JpZ2luYWwgcmVwb3J0cywgd2UgaGF2ZSBhIHB1bG1vbmFyeSBUQiBkZWF0aCByYXRlIHBlciBtaWxsaW9uIGZvciBhbGwgeWVhcnMsIGFuZCBudW1iZXJzIG9mIHB1bG1vbmFyeSBUQiBkZWF0aHMgZm9yIGVhY2ggeWVhciBhcGFydCBmcm9tIDE5NTAuCgpgYGB7cn0KCiNnZXQgdGhlIG92ZXJhbGwgbW9ydGFsaXR5IHNoZWV0cwpkZWF0aHNfc2hlZXRzIDwtIGVuZnJhbWUoYWxsX3NoZWV0cykgJT4lCiAgZmlsdGVyKGdyZXBsKCJkZWF0aHMiLCB2YWx1ZSkpICU+JQogIHB1bGwodmFsdWUpCgoKb3ZlcmFsbF9kZWF0aHMgPC0gbWFwX2RmKGRlYXRoc19zaGVldHMsIH5yZWFkX3hsc3gocGF0aCA9ICIyMDIzLTExLTI4X2dsYXNnb3ctYWNmLnhsc3giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoZWV0ID0gLikpCgpvdmVyYWxsX2RlYXRocyAlPiUKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpICYgISh5ZWFyKSwgIH5jb21tYSguKSkpICU+JQogIGRhdGF0YWJsZSgpCgoKCmBgYAoKUGxvdCB0aGUgcmF3IG51bWJlcnMgb2YgcHVsbW9uYXJ5IGRlYXRocwoKYGBge3J9CgpvdmVyYWxsX2RlYXRocyAlPiUKICBnZ3Bsb3QoYWVzKHg9eWVhciwgeT1wdWxtb25hcnlfZGVhdGhzKSkgKwogIGdlb21fbGluZShjb2xvdXIgPSAiI0RFMEQ5MiIpICsKICBnZW9tX3BvaW50KGNvbG91ciA9ICIjREUwRDkyIikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX3N0YXJ0KSwgbGluZXR5cGU9MykgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX2VuZCksIGxpbmV0eXBlPTMpICsKICBsYWJzKHk9IlB1bG1vbmFyeSBUQiBkZWF0aHMgcGVyIHllYXIiLAogICAgICAgeCA9ICJZZWFyIiwKICAgICAgIHRpdGxlID0gIk51bWJlcnMgb2YgcHVsbW9uYXJ5IFRCIGRlYXRocyIsCiAgICAgICBzdWJ0aXRsZSA9ICJHbGFzZ293LCAxOTUwLTE5NjMiLAogICAgY2FwdGlvbiA9ICJNYXNzIG1pbmlhdHVyZSBYLXJheSBjYW1wYWlnbiBwZXJpb2QgYmV0d2VlbiBkYXNoZWQgbGluZXMgKDExdGggTWFyY2gtMTJ0aCBBcHJpbCAxOTU3KVxuTm90ZTogbm8gZGF0YSBmb3IgMTk1MCIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkKCgpgYGAKCk5vdyB0aGUgaW5jaWRlbmNlIG9mIHB1bG1vbmFyeSBUQiBkZWF0aAoKYGBge3J9Cm92ZXJhbGxfZGVhdGhzICU+JQogIGdncGxvdChhZXMoeD15ZWFyLCB5PXB1bG1vbmFyeV9kZWF0aF9yYXRlX3Blcl8xMDBrKSkgKwogIGdlb21fbGluZShjb2xvdXIgPSAiIzRENkNGQSIpICsKICBnZW9tX3BvaW50KGNvbG91ciA9ICIjNEQ2Q0ZBIikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX3N0YXJ0KSwgbGluZXR5cGU9MykgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9YWNmX2VuZCksIGxpbmV0eXBlPTMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSB5ZWFyX2xhYmVscykgKwogIGxhYnMoeT0iQW5udWFsIGluY2lkZW5jZSBvZiBkZWF0aCAocGVyIDEwMCwwMDApIiwKICAgICAgIHggPSAiWWVhciIsCiAgICBjYXB0aW9uID0gIk1hc3MgbWluaWF0dXJlIFgtcmF5IGNhbXBhaWduIHBlcmlvZCBiZXR3ZWVuIGRhc2hlZCBsaW5lcyAoMTF0aCBNYXJjaC0xMnRoIEFwcmlsIDE5NTcpIikgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXk3OCIsIGZpbGw9TkEpKQoKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvczcucG5nIiksIHdpZHRoPTEwKQoKYGBgCgoKIyMjIDYuIFRhYmxlIDEKCk1ha2UgVGFibGUgMSBoZXJlLCBhbmQgc2F2ZSBmb3IgcHVibGljYXRpb24uCgpgYGB7cn0KCm92ZXJhbGxfcG9wcyAlPiUgCiAgc2VsZWN0KHllYXIsIHRvdGFsX3BvcHVsYXRpb24pICU+JQogIGxlZnRfam9pbihvdmVyYWxsX2luYyAlPiUKICAgICAgICAgICAgICBzZWxlY3QoeWVhciwgCiAgICAgICAgICAgICAgICAgICAgIHB1bG1vbmFyeV9ub3RpZmljYXRpb25zLCBpbmNfcHVsbV8xMDBrLAogICAgICAgICAgICAgICAgICAgICBgbm9uLXB1bG1vbmFyeV9ub3RpZmljYXRpb25zYCwgaW5jX2VwXzEwMGssCiAgICAgICAgICAgICAgICAgICAgIHRvdGFsX25vdGlmaWNhdGlvbnMsIGluY18xMDBrKSkgJT4lCiAgbGVmdF9qb2luKG92ZXJhbGxfZGVhdGhzICU+JQogICAgICAgICAgICAgIHNlbGVjdCh5ZWFyLAogICAgICAgICAgICAgICAgICAgICBwdWxtb25hcnlfZGVhdGhzLCBwdWxtb25hcnlfZGVhdGhfcmF0ZV9wZXJfMTAwaykpICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykgJiAhKHllYXIpLCAgfnJvdW5kKC4sIGRpZ2l0cz0xKSkpICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykgJiAhKHllYXIpLCAgfmNvbW1hKC4pKSkgCgpgYGAKCgpQcmVwYXJlIHRoZSBkYXRhc2V0cyBmb3IgbW9kZWxsaW5nCgpgYGB7cn0KCm1kYXRhIDwtIHdhcmRfaW5jICU+JQogIGZpbHRlcih0Yl90eXBlPT0iUHVsbW9uYXJ5IikgJT4lCiAgbXV0YXRlKGFjZl9wZXJpb2QgPSBjYXNlX3doZW4oeWVhciAlaW4lIGMoMTk1MDoxOTU2KSB+ICJhLiBwcmUtYWNmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyICVpbiUgYygxOTU3KSB+ICJiLiBhY2YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgJWluJSBjKDE5NTg6MTk2MykgfiAiYy4gcG9zdC1hY2YiKSkgJT4lCiAgZ3JvdXBfYnkod2FyZCkgJT4lCiAgbXV0YXRlKHlfbnVtID0gcm93X251bWJlcigpKSAlPiUKICB1bmdyb3VwKCkKCgptZGF0YV9leHRyYXB1bG1vbmFyeSA8LSB3YXJkX2luYyAlPiUKICBmaWx0ZXIodGJfdHlwZT09Ik5vbi1QdWxtb25hcnkiKSAlPiUKICBtdXRhdGUoYWNmX3BlcmlvZCA9IGNhc2Vfd2hlbih5ZWFyICVpbiUgYygxOTUwOjE5NTYpIH4gImEuIHByZS1hY2YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgJWluJSBjKDE5NTcpIH4gImIuIGFjZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVhciAlaW4lIGMoMTk1ODoxOTYzKSB+ICJjLiBwb3N0LWFjZiIpKSAlPiUKICBncm91cF9ieSh3YXJkKSAlPiUKICBtdXRhdGUoeV9udW0gPSByb3dfbnVtYmVyKCkpICU+JQogIHVuZ3JvdXAoKSAlPiUgCiAgZmlsdGVyKHllYXI8PTE5NjEpICNubyBkYXRhIGZvciAxOTYyIGFuZCAxOTYzCgoKI3NjYWZmb2xkIGZvciBvdmVyYWxsIHByZWRpY3Rpb25zCm92ZXJhbGxfc2NhZmZvbGQgPC0gbWRhdGEgJT4lCiAgICBzZWxlY3QoeWVhciwgeWVhcjIsIHlfbnVtLCBhY2ZfcGVyaW9kLCBwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwLCB3YXJkLCBjYXNlcykgJT4lCiAgICBncm91cF9ieSh5ZWFyLCB5ZWFyMiwgeV9udW0sIGFjZl9wZXJpb2QpICU+JQogICAgc3VtbWFyaXNlKHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAgPSBzdW0ocG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCksCiAgICAgICAgICAgICAgY2FzZXMgPSBzdW0oY2FzZXMpKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIG11dGF0ZShpbmNfMTAwayA9IGNhc2VzL3BvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAqMTAwMDAwKSAlPiUKICAgIGxlZnRfam9pbihtZGF0YV9leHRyYXB1bG1vbmFyeSAlPiUgZ3JvdXBfYnkoeWVhcikgJT4lCiAgICAgICAgICAgICAgICBzdW1tYXJpc2UoY2FzZXNfZXh0cmFwdWxtb25hcnkgPSBzdW0oY2FzZXMpKSkgJT4lCiAgICBtdXRhdGUoaW5jXzEwMGtfZXh0cmFwdWxtb25hcnkgPSBjYXNlc19leHRyYXB1bG1vbmFyeS9wb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwKjEwMDAwMCkKCmBgYAoKCiMjIyA3LiBQdWxtb25hcnkgVEIgbW9kZWwKCiMjIyMgNy4xIEZpdCB0aGUgbW9kZWwgYW5kIHByaW9ycwoKVGhpcyBtb2RlbHMgdGhlIGNhc2Ugbm90aWZpY2F0aW9uIHJhdGUgb3ZlciB0aW1lLCB3aXRoIGEgc3RlcCBjaGFuZ2UgZm9yIHRoZSBpbnRlcnZlbnRpb24sIGFuZCBzbG9wZSBjaGFuZ2UgYWZ0ZXIgdGhlIGludGVydmVudGlvbi4KCldvcmsgb24gdGhlIHByaW9ycyBhIGJpdC4gV2Ugd2lsbCBidWlsZCB1cCBmcm9tIGxlc3MgY29tcGxleCB0byBtb3JlIGNvbXBsZXguCgphKSBpbnRlcmNlcHQgb25seSwgdG8gcHJlZGljdCBjb3VudCBvZiBjYXNlcwoKYXQgdGhlIGludGVyY2VwdCwgd2UgZXhwZWN0IHNvbWV3aGVyZSBhcm91bmQgMjUwMC4gV2Ugd2lsbCBzZXQgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiB0byBib3RoIDAuNSBhbmQgMSB0byBjaGVjayB3aGF0IGl0IGxvb2tzIGxpa2UKCmBgYHtyfQojIAojIGMocHJpb3IobG9nbm9ybWFsKDcuNjAwOTAyLCAwLjUpKSwgI2xvZygyNTAwKSA9IDcuNjAwOTAyCiMgICBwcmlvcihsb2dub3JtYWwoNy42MDA5MDIsIDEpKSkgJT4lIAojICAgcGFyc2VfZGlzdCgpICU+JSAKIyAgIAojICAgZ2dwbG90KGFlcyh5ID0gcHJpb3IsIGRpc3QgPSAuZGlzdCwgYXJncyA9IC5hcmdzKSkgKwojICAgc3RhdF9oYWxmZXllKC53aWR0aCA9IGMoLjUsIC45NSkpICsKIyAgIHNjYWxlX3lfZGlzY3JldGUoTlVMTCwgbGFiZWxzID0gc3RyX2MoImxvZ25vcm1hbChsb2coMjAwMCksICIsIGMoMC41LCAxKSwgIikiKSwKIyAgICAgICAgICAgICAgICAgICAgZXhwYW5kID0gZXhwYW5zaW9uKGFkZCA9IDAuMSkpICsKIyAgIHhsYWIoZXhwcmVzc2lvbihleHAoaXRhbGljKHApKGJldGFbMF0pKSkpICsKIyAgIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLDE1MDAwKSkKIyAKIyAKIyBwcmlvcihnYW1tYSgxLCAwLjAxKSkgJT4lCiMgICBwYXJzZV9kaXN0KCkgJT4lCiMgICBnZ3Bsb3QoYWVzKHk9cHJpb3IsIGRpc3QgPSAuZGlzdCwgYXJncyA9IC5hcmdzKSkgKwojICAgc3RhdF9oYWxmZXllKC53aWR0aCA9IGMoMC41LCAwLjk1KSkKIyAKIyAjbm93IGZpdCB0byBhIG1vZGVsLCBhbmQgcGxvdCBzb21lIHByaW9yIHJlYWxpc2F0aW9ucwojIAojIG1fcHJpb3IxIDwtIGJybSgKIyAgIGNhc2VzIH4gMCArIEludGVyY2VwdCwKIyAgIGZhbWlseSA9IG5lZ2Jpbm9taWFsKCksCiMgICBkYXRhID0gb3ZlcmFsbF9zY2FmZm9sZCwKIyAgIHNhbXBsZV9wcmlvciA9ICJvbmx5IiwKIyAgIHByaW9yID0gcHJpb3Iobm9ybWFsKGxvZygyMDAwKSwgMC41KSwgY2xhc3MgPSBiLCBjb2VmID0gSW50ZXJjZXB0KSArCiMgICAgICAgICAgIHByaW9yKGdhbW1hKDEsIDAuMDEpLCBjbGFzcyA9IHNoYXBlKQojICkKIyAKIyBhZGRfZXByZWRfZHJhd3Mob2JqZWN0PW1fcHJpb3IxLAojICAgICAgICAgICAgICAgICBuZXdkYXRhID0gdGliYmxlKGludGVyY2VwdD0xKSkgJT4lCiMgICBnZ3Bsb3QoYWVzKHg9aW50ZXJjZXB0LCB5PS5lcHJlZCkpICsKIyAgIHN0YXRfaGFsZmV5ZSgpICsKIyAgIHNjYWxlX3lfbG9nMTAobGFiZWxzID0gY29tbWEpCgoKYGBgCgpOb3cgdHJ5IHRvIGFkZCBpbiBhIHRlcm0gZm9yIHRoZSBlZmZlY3Qgb2YgeV9udW0uIFdlIGFudGljcGF0ZSB0aGF0IHRoZSBudW1iZXIgb2YgY2FzZXMgd2lsbCBkZWNsaW5lIGJ5IGFib3V0IDEtNSUgcGVyIHllYXIuIEhvd2V2ZXIsIGFzIHdlIGFyZSBwcmV0dHkgdW5jZXJ0YWluIGFib3V0IHRoaXMsIHdlIHdpbGwganVzdCBlbmNvZGUgYSB3ZWFrbHkgcmVndWxhcmlzaW5nIHByaW9yIHRvIHJlc3RyaWN0IHRoZSB5ZWFyIHNpemUgdG8gc2Vuc2libGUgcmFuZ2VzLgoKYGBge3J9CiMgCiMgCiMgbV9wcmlvcjIgPC0gYnJtKAojICAgY2FzZXMgfiAwICsgSW50ZXJjZXB0ICsgeV9udW0sCiMgICBmYW1pbHkgPSBuZWdiaW5vbWlhbCgpLAojICAgZGF0YSA9IG92ZXJhbGxfc2NhZmZvbGQsCiMgICBzYW1wbGVfcHJpb3IgPSAib25seSIsCiMgICBwcmlvciA9IHByaW9yKG5vcm1hbChsb2coMjAwMCksIDAuNSksIGNsYXNzID0gYiwgY29lZiA9IEludGVyY2VwdCkgKwojICAgICAgICAgICBwcmlvcihnYW1tYSgxLCAwLjAxKSwgY2xhc3MgPSBzaGFwZSkgKwojICAgICAgICAgICBwcmlvcihub3JtYWwoMCwgMC4wMSksIGNsYXNzID0gYiwgY29lZiA9IHlfbnVtKQojICkKIyAKIyBhZGRfZXByZWRfZHJhd3Mob2JqZWN0PW1fcHJpb3IyLAojICAgICAgICAgICAgICAgICBuZXdkYXRhID0gb3ZlcmFsbF9zY2FmZm9sZCkgJT4lCiMgICBnZ3Bsb3QoYWVzKHg9eWVhciwgeT0uZXByZWQpKSArCiMgICBzdGF0X2hhbGZleWUoKSArCiMgICBzY2FsZV95X2xvZzEwKGxhYmVsPWNvbW1hKQoKYGBgCgpOb3cgd2Ugd2FudCB0byBhZGQgaW4gYSBwcmlvciBmb3IgdGhlIGVmZmVjdCBvZiB0aGUgYWNmX2ludGVydmVudGlvbi4gV2UgYW50aWNpcGF0ZSB0aGUgcGVhayB0byBiZSBhbnl3aGVyZSBiZXR3ZWVuIG5vIGVmZmVjdCwgYW5kIGEgdHJpcGxpbmcKCmBgYHtyfQojIAojIG1fcHJpb3IzIDwtIGJybSgKIyAgIGNhc2VzIH4gMCArIEludGVyY2VwdCArIHlfbnVtICsgYWNmX3BlcmlvZCwKIyAgIGZhbWlseSA9IG5lZ2Jpbm9taWFsKCksCiMgICBkYXRhID0gb3ZlcmFsbF9zY2FmZm9sZCwKIyAgIHNhbXBsZV9wcmlvciA9ICJvbmx5IiwKIyAgIHByaW9yID0gcHJpb3Iobm9ybWFsKGxvZygyMDAwKSwgMC41KSwgY2xhc3MgPSBiLCBjb2VmID0gSW50ZXJjZXB0KSArCiMgICAgICAgICAgIHByaW9yKGdhbW1hKDEsIDAuMDEpLCBjbGFzcyA9IHNoYXBlKSArCiMgICAgICAgICAgIHByaW9yKG5vcm1hbCgwLCAwLjAxKSwgY2xhc3MgPSBiLCBjb2VmID0geV9udW0pICsKIyAgICAgICAgICAgcHJpb3Iobm9ybWFsKDAsIDAuMDAxKSwgY2xhc3MgPSBiKQojICkKIyAKIyAKIyBhZGRfZXByZWRfZHJhd3Mob2JqZWN0PW1fcHJpb3IzLAojICAgICAgICAgICAgICAgICBuZXdkYXRhID0gb3ZlcmFsbF9zY2FmZm9sZCkgJT4lCiMgICBnZ3Bsb3QoYWVzKHg9eWVhciwgeT0uZXByZWQpKSArCiMgICBzdGF0X2hhbGZleWUoKSArCiMgICBzY2FsZV95X2xvZzEwKGxhYmVscyA9IGNvbW1hKQojIAoKCmBgYAoKTm93IHdlIGxvb2sgYW5kIHNlZSB3aGF0IGl0IGxvb2tzIGxpa2Ugd2l0aCB0aGUgaW50ZXJhY3Rpb25zCgpgYGB7cn0KIyAKIyBtX3ByaW9yNCA8LSBicm0oCiMgICBjYXNlcyB+IDAgKyBJbnRlcmNlcHQgKyB5X251bSArIGFjZl9wZXJpb2QgKyB5X251bTphY2ZfcGVyaW9kLAojICAgZmFtaWx5ID0gbmVnYmlub21pYWwoKSwKIyAgIGRhdGEgPSBvdmVyYWxsX3NjYWZmb2xkLAojICAgc2FtcGxlX3ByaW9yID0gIm9ubHkiLAojICAgcHJpb3IgPSBwcmlvcihub3JtYWwobG9nKDI1MDApLCAxKSwgY2xhc3MgPSBiLCBjb2VmID0gSW50ZXJjZXB0KSArCiMgICAgICAgICAgIHByaW9yKGdhbW1hKDEsIDAuMDEpLCBjbGFzcyA9IHNoYXBlKSArCiMgICAgICAgICAgIHByaW9yKG5vcm1hbCgwLCAwLjAxKSwgY2xhc3MgPSBiKQojICkKIyAKIyBhZGRfZXByZWRfZHJhd3Mob2JqZWN0PW1fcHJpb3I0LAojICAgICAgICAgICAgICAgICBuZXdkYXRhID0gb3ZlcmFsbF9zY2FmZm9sZCkgJT4lCiMgICBnZ3Bsb3QoYWVzKHg9eWVhciwgeT0uZXByZWQpKSArCiMgICBzdGF0X2hhbGZleWUoKSArCiMgICBzY2FsZV95X2xvZzEwKGxhYmVsPWNvbW1hKQojIAojIAoKYGBgCgpOb3cgdHJ5IGFkZGluZyBpbiB0aGUgcmFuZG9tIGludGVyY2VwdHMKCmBgYHtyfQoKIyBjKHByaW9yKGxvZ25vcm1hbCgzLjkxMjAyMywgMC41KSksICNsb2coNTApID0gMy45MTIwMjMKIyAgIHByaW9yKGxvZ25vcm1hbCgzLjkxMjAyMywgMSkpKSAlPiUgCiMgICBwYXJzZV9kaXN0KCkgJT4lIAojICAgCiMgICBnZ3Bsb3QoYWVzKHkgPSBwcmlvciwgZGlzdCA9IC5kaXN0LCBhcmdzID0gLmFyZ3MpKSArCiMgICBzdGF0X2hhbGZleWUoLndpZHRoID0gYyguNSwgLjk1KSkgKwojICAgc2NhbGVfeV9kaXNjcmV0ZShOVUxMLCBsYWJlbHMgPSBzdHJfYygibG9nbm9ybWFsKGxvZyg1MCksICIsIGMoMC41LCAxKSwgIikiKSwKIyAgICAgICAgICAgICAgICAgICAgZXhwYW5kID0gZXhwYW5zaW9uKGFkZCA9IDAuMSkpICsKIyAgIHhsYWIoZXhwcmVzc2lvbihleHAoaXRhbGljKHApKGJldGFbMF0pKSkpICsKIyAgIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLDQwMCkpCiMgCiMgCiMgbV9wcmlvcjUgPC0gYnJtKAojICAgY2FzZXMgfiB5X251bSArIGFjZl9wZXJpb2QgKyB5X251bTphY2ZfcGVyaW9kICsgKCAxIHwgd2FyZCksCiMgICBmYW1pbHkgPSBuZWdiaW5vbWlhbCgpLAojICAgZGF0YSA9IG1kYXRhLAojICAgc2FtcGxlX3ByaW9yID0gIm9ubHkiLAojICAgcHJpb3IgPSBwcmlvcihub3JtYWwobG9nKDUwKSwgMSksIGNsYXNzID0gSW50ZXJjZXB0KSArCiMgICAgICAgICAgIHByaW9yKGdhbW1hKDEsIDAuMDEpLCBjbGFzcyA9IHNoYXBlKSArCiMgICAgICAgICAgIHByaW9yKG5vcm1hbCgwLCAwLjAxKSwgY2xhc3MgPSBiKSArCiMgICAgICAgICAgIHByaW9yKGV4cG9uZW50aWFsKDEpLCBjbGFzcz1zZCkKIyApCiMgCiMgCiMgYWRkX2VwcmVkX2RyYXdzKG9iamVjdD1tX3ByaW9yNSwKIyAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IG1kYXRhLAojICAgICAgICAgICAgICAgICByZV9mb3JtdWxhID0gTkEpICU+JQojICAgZ2dwbG90KGFlcyh4PXllYXIsIHk9LmVwcmVkKSkgKwojICAgc3RhdF9oYWxmZXllKCkgKwojICAgc2NhbGVfeV9sb2cxMChsYWJlbD1jb21tYSkKIyAKIyBhZGRfZXByZWRfZHJhd3Mob2JqZWN0PW1fcHJpb3I1LAojICAgICAgICAgICAgICAgICBuZXdkYXRhID0gbWRhdGEsCiMgICAgICAgICAgICAgICAgIHJlX2Zvcm11bGEgPSBOQSkgJT4lCiMgICBnZ3Bsb3QoYWVzKHg9eWVhciwgeT0uZXByZWQpKSArCiMgICBzdGF0X2hhbGZleWUoKSArCiMgICBzY2FsZV95X2xvZzEwKGxhYmVsPWNvbW1hKSArCiMgICBmYWNldF93cmFwKHdhcmR+LikKCmBgYAoKQW5kIGFkZCBpbiB0aGUgcmFuZG9tIHNsb3BlcwoKYGBge3J9CiMgCiMgbV9wcmlvcjYgPC0gYnJtKAojICAgY2FzZXMgfiAxICsgeV9udW0gKyBhY2ZfcGVyaW9kICsgeV9udW06YWNmX3BlcmlvZCArICgxICsgeV9udW0qYWNmX3BlcmlvZCB8IHdhcmQpLAojICAgZmFtaWx5ID0gbmVnYmlub21pYWwoKSwKIyAgIGRhdGEgPSBtZGF0YSwKIyAgIHNhbXBsZV9wcmlvciA9ICJvbmx5IiwKIyAgIHByaW9yID0gcHJpb3IoZ2FtbWEoMSwgMC4wMSksIGNsYXNzID0gc2hhcGUpICsKIyAgICAgICAgICAgcHJpb3Iobm9ybWFsKDAsIDAuMSksIGNsYXNzID0gYikgKwojICAgICAgICAgICBwcmlvcihleHBvbmVudGlhbCgxKSwgY2xhc3M9c2QpICsKIyAgICAgICAgICAgcHJpb3IobGtqKDIpLCBjbGFzcz1jb3IpCiMgKQojIAojIAojIAojIG1fcHJpb3I2IDwtIGJybSgKIyAgIGNhc2VzIH4gMCArIEludGVyY2VwdCArIHlfbnVtICsgYWNmX3BlcmlvZCArIHlfbnVtOmFjZl9wZXJpb2QgKyAoIHlfbnVtKmFjZl9wZXJpb2QgfCB3YXJkKSwKIyAgIGZhbWlseSA9IG5lZ2Jpbm9taWFsKCksCiMgICBkYXRhID0gbWRhdGEsCiMgICBzYW1wbGVfcHJpb3IgPSAib25seSIsCiMgICBwcmlvciA9IHByaW9yKG5vcm1hbChsb2coNTApLCAxKSwgY2xhc3MgPSBiLCBjb2VmID0gSW50ZXJjZXB0KSArCiMgICAgICAgICAgIHByaW9yKGdhbW1hKDEsIDAuMDEpLCBjbGFzcyA9IHNoYXBlKSArCiMgICAgICAgICAgIHByaW9yKG5vcm1hbCgwLCAwLjAxKSwgY2xhc3MgPSBiKSArCiMgICAgICAgICAgIHByaW9yKGV4cG9uZW50aWFsKDEwMCksIGNsYXNzPXNkKSArCiMgICAgICAgICAgIHByaW9yKGxraigyKSwgY2xhc3M9Y29yKQojICkKCgojIGFkZF9lcHJlZF9kcmF3cyhvYmplY3Q9bV9wcmlvcjYsCiMgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBtZGF0YSwKIyAgICAgICAgICAgICAgICAgcmVfZm9ybXVsYSA9IE5BKSAlPiUKIyAgIGdncGxvdChhZXMoeD15ZWFyLCB5PS5lcHJlZCkpICsKIyAgIHN0YXRfaGFsZmV5ZSgpICsKIyAgIHNjYWxlX3lfbG9nMTAobGFiZWw9Y29tbWEpCiMgCiMgYWRkX2VwcmVkX2RyYXdzKG9iamVjdD1tX3ByaW9yNiwKIyAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IG1kYXRhLAojICAgICAgICAgICAgICAgICByZV9mb3JtdWxhID0gfiggMSArIHlfbnVtICsgYWNmX3BlcmlvZCB8IHdhcmQpKSAlPiUKIyAgIGdncGxvdChhZXMoeD15ZWFyLCB5PS5lcHJlZCkpICsKIyAgIHN0YXRfaGFsZmV5ZSgpICsKIyAgIHNjYWxlX3lfbG9nMTAobGFiZWw9Y29tbWEpICsKIyAgIGZhY2V0X3dyYXAod2FyZH4uKQojIAojIHBsb3RfY291bnRlcmZhY3R1YWwobW9kZWxfZGF0YSA9IG92ZXJhbGxfc2NhZmZvbGQsIG1vZGVsPW1fcHJpb3I2LCBvdXRjb21lID0gaW5jXzEwMGssIAojICAgICAgICAgICAgICAgICAgICAgcG9wdWxhdGlvbl9kZW5vbWluYXRvciA9IHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAsIHJlX2Zvcm11bGEgPSBOQSkKIyAKIyBwbG90X2NvdW50ZXJmYWN0dWFsKG1vZGVsX2RhdGEgPSBtZGF0YSwgbW9kZWw9bV9wcmlvcjYsIG91dGNvbWUgPSBpbmNfMTAwaywgCiMgICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yID0gcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgZ3JvdXBpbmdfdmFyID0gd2FyZCwgd2FyZCwKIyAgICAgICAgICAgICAgICAgICAgIHJlX2Zvcm11bGEgPSB+KCAxICsgeV9udW0gKyBhY2ZfcGVyaW9kIHwgd2FyZCkpCgpgYGAKCgpJc3N1ZSBoZXJlIGlzIHRoZSBub24tY2VudHJlZCBwYXJhbWV0ZXJpc2F0aW9uIG9mIHRoZSBpbnRlcmNlcHQgcHJpb3IuLi4gRmVlbCBsaWtlIHRoaXMgaXMgYSBtb3JlIGludGVycHJldGFibGUgd2F5IHRvIHNldCBwcmlvcnMuLi4gYnV0IHdpbGwgcmV2ZXJ0IHRvIGNlbnRyZWQgcGFyYW1ldGVyaXNhdGlvbiBmb3IgdGhlIG1lYW50aW1lLgoKCmBgYHtyfQojIG1fY2VudGVyZWRfcHJpb3IgPC0gYnJtKAojICAgY2FzZXMgfiAxICsgeV9udW0qYWNmX3BlcmlvZCArICgxICsgeV9udW0qYWNmX3BlcmlvZCB8IHdhcmQpICsgb2Zmc2V0KGxvZyhwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwKSksCiMgICAgICAgICAgICAgICAgICAgZGF0YSA9IG1kYXRhLAojICAgICAgICAgICAgICAgICAgIGZhbWlseSA9IG5lZ2Jpbm9taWFsKCksCiMgICAgICAgICAgICAgICAgICAgc2VlZCA9IDEyMzQsCiMgICAgICAgICAgICAgICAgICAgY2hhaW5zID0gNCwgY29yZXMgPSA0LAojICAgICAgICAgICAgICAgICAgIHByaW9yID0gcHJpb3Iobm9ybWFsKDAsMTAwMCksIGNsYXNzID0gSW50ZXJjZXB0KSArCiMgICAgICAgICAgICAgICAgICAgICAgICAgICBwcmlvcihnYW1tYSgwLjAxLCAwLjAxKSwgY2xhc3MgPSBzaGFwZSkgKwojICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpb3Iobm9ybWFsKDAsIDEpLCBjbGFzcyA9IGIpICsKIyAgICAgICAgICAgICAgICAgICAgICAgICAgIHByaW9yKGV4cG9uZW50aWFsKDEpLCBjbGFzcz1zZCkgKwojICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpb3IobGtqKDIpLCBjbGFzcz1jb3IpLAojICAgICAgICAgICAgICAgICAgIHNhbXBsZV9wcmlvciA9ICJvbmx5IikKIyAKIyBwbG90KG1fY2VudGVyZWRfcHJpb3IpCiMgCiMgcGxvdF9jb3VudGVyZmFjdHVhbChtb2RlbF9kYXRhID0gb3ZlcmFsbF9zY2FmZm9sZCwgbW9kZWw9bV9jZW50ZXJlZF9wcmlvciwgb3V0Y29tZSA9IGluY18xMDBrLCAKIyAgICAgICAgICAgICAgICAgICAgIHBvcHVsYXRpb25fZGVub21pbmF0b3IgPSBwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwLCByZV9mb3JtdWxhID0gTkEpCiMgCiMgcGxvdF9jb3VudGVyZmFjdHVhbChtb2RlbF9kYXRhID0gbWRhdGEsIG1vZGVsPW1fY2VudGVyZWRfcHJpb3IsIG91dGNvbWUgPSBpbmNfMTAwaywgCiMgICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yID0gcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgZ3JvdXBpbmdfdmFyID0gd2FyZCwgd2FyZCwKIyAgICAgICAgICAgICAgICAgICAgIHJlX2Zvcm11bGEgPSB+KCAxICsgeV9udW0qYWNmX3BlcmlvZCB8IHdhcmQpKQoKYGBgCgoKCgpMb29rIGF0IHRoZSBtZWFuIGFuZCB2YXJpYW5jZSBvZiBjb3VudHMgKGNvdW50cyBvZiBwdWxtb25hcnkgbm90aWZpY2F0aW9ucyBhcmUgd2hhdCB3ZSBhcmUgcHJlZGljdGluZykKCmBgYHtyfQoKI01lYW4gb2YgY291bnRzIHBlciB5ZWFyCm1lYW4obWRhdGEkY2FzZXMpCiN2YXJpYW5jZSBvZiBjb3VudHMgcGVyIHllYXIKdmFyKG1kYXRhJGNhc2VzKQoKYGBgCgoKCgoKUXVpdGUgYSBiaXQgb2Ygb3Zlci1kaXNwZXJzaW9uIGhlcmUsIHNvIG5lZ2F0aXZlIGJpbm9taWFsIGRpc3RyaWJ1dGlvbiBtaWdodCBiZSBhIGJldHRlciBjaG9pY2Ugb2YgZGlzdHJpYnV0aW9uYWwgZmFtaWx5IHRoYW4gUG9pc3Nvbi4KCkZpdCB0aGUgbW9kZWwgd2l0aCB0aGUgZGF0YQoKYGBge3J9CgptX3B1bG1vbmFyeSA8LSBicm0oCiAgY2FzZXMgfiAxICsgeV9udW0qYWNmX3BlcmlvZCArICgxICsgeV9udW0qYWNmX3BlcmlvZCB8IHdhcmQpICsgb2Zmc2V0KGxvZyhwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwKSksCiAgICAgICAgICAgICAgICAgIGRhdGEgPSBtZGF0YSwKICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gbmVnYmlub21pYWwoKSwKICAgICAgICAgICAgICAgICAgc2VlZCA9IDEyMzQsCiAgICAgICAgICAgICAgICAgIGNoYWlucyA9IDQsIGNvcmVzID0gNCwKICAgICAgICAgICAgICAgICAgcHJpb3IgPSBwcmlvcihub3JtYWwoMCwxKSwgY2xhc3MgPSBJbnRlcmNlcHQpICsKICAgICAgICAgICAgICAgICAgICAgICAgICBwcmlvcihnYW1tYSgwLjAxLCAwLjAxKSwgY2xhc3MgPSBzaGFwZSkgKwogICAgICAgICAgICAgICAgICAgICAgICAgIHByaW9yKG5vcm1hbCgwLCAxKSwgY2xhc3MgPSBiKSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpb3IoZXhwb25lbnRpYWwoMSksIGNsYXNzPXNkKSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpb3IobGtqKDQpLCBjbGFzcz1jb3IpLAogIGNvbnRyb2wgPSBsaXN0KGFkYXB0X2RlbHRhID0gMC45KSkKICAKI2NoZWNrIG1vZGVsIGRpYWdub3N0aWNzCnN1bW1hcnkobV9wdWxtb25hcnkpCnBsb3QobV9wdWxtb25hcnkpCgpwcF9jaGVjayhtX3B1bG1vbmFyeSwgdHlwZT0nZWNkZl9vdmVybGF5JykKcHJpb3Jfc3VtbWFyeShtX3B1bG1vbmFyeSkKCmBgYAoKCiMjIyMgNy4yIFN1bW1hcmlzZSBjaGFuZ2UgaW4gQ05ScwoKU3VtbWFyaXNlIHRoZSBwb3N0ZXJpb3IgaW4gZ3JhcGhpY2FsIGZvcm0KCmBgYHtyfQoKZjFiIDwtIHBsb3RfY291bnRlcmZhY3R1YWwobW9kZWxfZGF0YSA9IG92ZXJhbGxfc2NhZmZvbGQsIG1vZGVsID0gbV9wdWxtb25hcnksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yID0gcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgb3V0Y29tZSA9IGluY18xMDBrLCBncm91cGluZ192YXI9TlVMTCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVfZm9ybXVsYSA9IE5BKQogIApmMWIKYGBgCgpNYWtlIHRoaXMgaW50byBhIGZpZ3VyZSBjb21iaW5lZCB3aXRoIHRoZSBtYXAgb2YgZW1waXJpY2FsIGRhdGEKCmBgYHtyfQoKZjFhIDwtIHN0X2FzX3NmKGxlZnRfam9pbih3YXJkX2luYywgZ2xhc2dvd193YXJkc18xOTUxKSkgJT4lCiAgZmlsdGVyKHRiX3R5cGU9PSJQdWxtb25hcnkiKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9zZihhZXMoZmlsbD1pbmNfMTAwaykpICsKICBmYWNldF93cmFwKHllYXJ+LiwgbmNvbCA9IDcpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhuYW1lPSJDYXNlIG5vdGlmaWNhdGlvbiByYXRlIChwZXIgMTAwLDAwMCkiLAogICAgICAgICAgICAgICAgICAgICAgIG9wdGlvbiA9ICJBIikgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXk3OCIsIGZpbGw9TkEpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICAgIGxlZ2VuZC5rZXkud2lkdGggPSB1bml0KDIsICJjbSIpLAogICAgICAgIGxlZ2VuZC50aXRsZS5hbGlnbiA9IDAuNSwKICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUsIHNpemU9NiwgaGp1c3Q9MSksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT02KSkgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2NvbG9yYmFyKHRpdGxlLnBvc2l0aW9uID0gInRvcCIpKQoKKGYxYSAvIGYxYikgKyBwbG90X2Fubm90YXRpb24odGFnX2xldmVscyA9ICJBIikKCmdnc2F2ZShoZXJlKCJmaWd1cmVzL2YxLnBuZyIpKQoKYGBgCgpTdW1tYXJ5IG9mIGNoYW5nZSBpbiBub3RpZmljYXRpb25zIG51bWVyaWNhbGx5CgpgYGB7cn0KCm92ZXJhbGxfY2hhbmdlIDwtIHN1bW1hcmlzZV9jaGFuZ2UobW9kZWxfZGF0YT1vdmVyYWxsX3NjYWZmb2xkLCBtb2RlbD1tX3B1bG1vbmFyeSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9wdWxhdGlvbl9kZW5vbWluYXRvcj1wb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwLCBncm91cGluZ192YXI9TlVMTCwgcmVfZm9ybXVsYSA9IE5BKQoKI3dhbnQgdG8ga2VlcCB0aGUgc3VtbWFyeSBlc3RpbWF0ZXMgaGVyZQp0b2tlZXAgPC0gYygicGVha19zdW1tYXJ5IiwgImxldmVsX3N1bW1hcnkiLCAic2xvcGVfc3VtbWFyeSIpCgojc3VtbWFyeSBtZWFzdXJlcyBpbiBhIHRhYmxlCm92ZXJhbGxfY2hhbmdlICU+JQogIGtlZXAoKG5hbWVzKC4pICVpbiUgdG9rZWVwKSkgJT4lCiAgYmluZF9yb3dzKCkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKGVzdGltYXRlOi51cHBlciksIG51bWJlciwgYWNjdXJhY3k9MC4wMSkpICU+JQogIHNlbGVjdChtZWFzdXJlLCBldmVyeXRoaW5nKCkpICU+JQogIGRhdGF0YWJsZSgpCgogIApgYGAKCgojIyMjIDcuMyBDb21wYXJlZCB0byBjb3VudGVyZmFjdHVhbAoKTnVtYmVycyBvZiBwdWxtb25hcnkgVEIgY2FzZXMgYXZlcnRlZCBjb21wYXJlZCB0byBjb3VudGVyZmFjdHVhbCBwZXIgeWVhci4KCmBgYHtyfQoKb3ZlcmFsbF9wdWxtb25hcnlfY291bnRlcmYgPC0gY2FsY3VsYXRlX2NvdW50ZXJmYWN0dWFsKG1vZGVsX2RhdGEgPSBvdmVyYWxsX3NjYWZmb2xkLCBtb2RlbD1tX3B1bG1vbmFyeSwgcG9wdWxhdGlvbl9kZW5vbWluYXRvciA9IHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXApCgpvdmVyYWxsX3B1bG1vbmFyeV9jb3VudGVyZiRjb3VudGVyX3Bvc3QgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKGNhc2VzX2F2ZXJ0ZWQ6Y2FzZXNfYXZlcnRlZC51cHBlciwgZGlmZl9pbmMxMDBrOmRpZmZfaW5jMTAway51cHBlciksIG51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjEsIGJpZy5tYXJrID0gIiwiKSkpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhycl9pbmMxMDBrOnJyX2luYzEwMGsudXBwZXIpLCBudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4wMSkpKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMocGN0X2NoYW5nZTpwY3RfY2hhbmdlLnVwcGVyKSwgcGVyY2VudCwgYWNjdXJhY3k9MC4xKSkgJT4lCiAgZGF0YXRhYmxlKCkKCgpgYGAKClRvdGFsIHB1bG1vbmFyeSBUQiBjYXNlcyBhdmVydGVkIGJldHdlZW4gMTk1OCBhbmQgMTk2MwoKYGBge3J9CgpvdmVyYWxsX3B1bG1vbmFyeV9jb3VudGVyZiRjb3VudGVyX3Bvc3Rfb3ZlcmFsbCAlPiUKICBtdXRhdGUoYWNyb3NzKGMoY2FzZXNfYXZlcnRlZDpjYXNlc19hdmVydGVkLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMSwgYmlnLm1hcmsgPSAiLCIpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHBjdF9jaGFuZ2U6cGN0X2NoYW5nZS51cHBlciksIHBlcmNlbnQsIGFjY3VyYWN5PTAuMSkpICU+JQogIGRhdGF0YWJsZSgpCgoKYGBgCgojIyMjIDcuNCBDb3JyZWxhdGlvbiBiZXR3ZWVuIFJSLnBlYWssIFJSLmxldmVsLCBhbmQgUlIuc2xvcGUKCldoYXQgYXJlIHRoZSBjb3JyZWxhdGlvbnMgYmV0d2VlbiBwZWFrLCBsZXZlbCwgYW5kIHNsb3BlPwoKYGBge3J9CgojUlIucGVhayBoaXN0b2dyYW0KYSA8LSBvdmVyYWxsX2NoYW5nZSRwZWFrX2RyYXdzICU+JQogIGdncGxvdCgpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeD1lc3RpbWF0ZSksIGZpbGw9ImRhcmtibHVlIiwgY29sb3VyPSJkYXJrYmx1ZSIsIGFscGhhPTAuMykrCiAgc2NhbGVfZmlsbF9ncmFkaWVudChoaWdoPSJsaWdodGJsdWUxIixsb3c9ImRhcmtibHVlIikgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkgKwogIGxhYnMoeD0iUlIucGVhayIsCiAgICAgICB5PSIiKQoKI1JSLiBsZXZlbCBoaXN0b2dyYW0KYiA8LSBvdmVyYWxsX2NoYW5nZSRsZXZlbF9kcmF3cyAgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21faGlzdG9ncmFtKGFlcyh4PWVzdGltYXRlKSwgZmlsbD0iZGFya2JsdWUiLCBjb2xvdXI9ImRhcmtibHVlIiwgYWxwaGE9MC4zKSsKICBzY2FsZV9maWxsX2dyYWRpZW50KGhpZ2g9ImxpZ2h0Ymx1ZTEiLGxvdz0iZGFya2JsdWUiKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXk3OCIsIGZpbGw9TkEpKSArCiAgbGFicyh4PSJSUi5sZXZlbCIsCiAgICAgICB5PSIiKQoKI1JSLnNsb3BlIGhpc3RvZ3JhbQpjIDwtIG92ZXJhbGxfY2hhbmdlJHNsb3BlX2RyYXdzICU+JQogIGdncGxvdCgpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeD1lc3RpbWF0ZSksIGZpbGw9ImRhcmtibHVlIiwgY29sb3VyPSJkYXJrYmx1ZSIsIGFscGhhPTAuMykrCiAgc2NhbGVfZmlsbF9ncmFkaWVudChoaWdoPSJsaWdodGJsdWUxIixsb3c9ImRhcmtibHVlIikgKyAgCiAgI3NjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDYpKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXk3OCIsIGZpbGw9TkEpKSArCiAgbGFicyh4PSJSUi5zbG9wZSAobG9naXQgc2NhbGUpIiwKICAgICAgIHk9IiIpCgoKI0NvcnJlbGF0aW9uIGJldHdlZW4gUlIucGVhayBhbmQgUlIubGV2ZWwKY29yX3JyX3BlYWtfcnJfbGV2ZWwgPC0gcm91bmQoY29yKHBsdWNrKG92ZXJhbGxfY2hhbmdlJHBlYWtfZHJhd3MkZXN0aW1hdGUpLCBwbHVjayhvdmVyYWxsX2NoYW5nZSRsZXZlbF9kcmF3cyRlc3RpbWF0ZSkpLCBkaWdpdHMgPSAyKQoKI0NvcnJlbGF0aW9uIGJldHdlZW4gUlIucGVhayBhbmQgUlIuc2xvcGUKY29yX3JyX3BlYWtfcnJfc2xvcGUgPC0gcm91bmQoY29yKHBsdWNrKG92ZXJhbGxfY2hhbmdlJHBlYWtfZHJhd3MkZXN0aW1hdGUpLCBwbHVjayhvdmVyYWxsX2NoYW5nZSRzbG9wZV9kcmF3cyRlc3RpbWF0ZSkpLCBkaWdpdHMgPSAyKQoKI0NvcnJlbGF0aW9uIGJldHdlZW4gUlIubGV2ZWwgYW5kIFJSLnNsb3BlCmNvcl9ycl9sZXZlbF9ycl9zbG9wZSA8LSByb3VuZChjb3IocGx1Y2sob3ZlcmFsbF9jaGFuZ2UkbGV2ZWxfZHJhd3MkZXN0aW1hdGUpLCBwbHVjayhvdmVyYWxsX2NoYW5nZSRzbG9wZV9kcmF3cyRlc3RpbWF0ZSkpLCBkaWdpdHMgPSAyKQoKCiNwbG90IG9mIGNvcnJlbGF0aW9uIGJldHdlZW4gUlIucGVhayBhbmQgUlIubGV2ZWwKZCA8LSBiaW5kX2NvbHMoUlIucGVhaz1wbHVjayhvdmVyYWxsX2NoYW5nZSRwZWFrX2RyYXdzJGVzdGltYXRlKSwgCiAgICAgICAgICBSUi5sZXZlbCA9cGx1Y2sob3ZlcmFsbF9jaGFuZ2UkbGV2ZWxfZHJhd3MkZXN0aW1hdGUpKSAlPiUKICBnZ3Bsb3QoYWVzKHk9UlIucGVhaywgeCA9IFJSLmxldmVsKSkgKwogIGdlb21faGV4KCkgKwogIGdlb21fc21vb3RoKHNlPUZBTFNFLCBjb2xvdXI9ImZpcmVicmljayIsIG1ldGhvZCA9ICJsbSIpICsKICBnZW9tX3RleHQoYWVzKHk9Mi4yLCB4PTAuNTgsIGxhYmVsPWNvcl9ycl9wZWFrX3JyX2xldmVsKSwgY29sb3VyPSJmaXJlYnJpY2siKSAgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQoaGlnaD0ibGlnaHRibHVlMSIsbG93PSJkYXJrYmx1ZSIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSkpCgojcGxvdCBvZiBjb3JyZWxhdGlvbiBiZXR3ZWVuIFJSLnBlYWsgYW5kIFJSLnNsb3BlCmUgPC0gYmluZF9jb2xzKFJSLnBlYWs9cGx1Y2sob3ZlcmFsbF9jaGFuZ2UkcGVha19kcmF3cyRlc3RpbWF0ZSksIAogICAgICAgICAgUlIuc2xvcGUgPXBsdWNrKG92ZXJhbGxfY2hhbmdlJHNsb3BlX2RyYXdzJGVzdGltYXRlKSkgJT4lCiAgZ2dwbG90KGFlcyh5PVJSLnBlYWssIHggPSBSUi5zbG9wZSkpICsKICBnZW9tX2hleCgpICsKICBnZW9tX3Ntb290aChzZT1GQUxTRSwgY29sb3VyPSJmaXJlYnJpY2siLCBtZXRob2QgPSAibG0iKSArCiAgZ2VvbV90ZXh0KGFlcyh5PTIuMSwgeD0wLjUsIGxhYmVsPWNvcl9ycl9wZWFrX3JyX3Nsb3BlKSwgY29sb3VyPSJmaXJlYnJpY2siKSAgKwogICNzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCA2KSkgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQoaGlnaD0ibGlnaHRibHVlMSIsbG93PSJkYXJrYmx1ZSIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSkpCgojcGxvdCBvZiBjb3JyZWxhdGlvbiBiZXR3ZWVuIFJSLmxldmVsIGFuZCBSUi5zbG9wZQpmIDwtIGJpbmRfY29scyhSUi5sZXZlbD1wbHVjayhvdmVyYWxsX2NoYW5nZSRsZXZlbF9kcmF3cyRlc3RpbWF0ZSksIAogICAgICAgICAgUlIuc2xvcGUgPXBsdWNrKG92ZXJhbGxfY2hhbmdlJHNsb3BlX2RyYXdzJGVzdGltYXRlKSkgJT4lCiAgZ2dwbG90KGFlcyh5PVJSLmxldmVsLCB4ID0gUlIuc2xvcGUpKSArCiAgZ2VvbV9oZXgoKSArCiAgZ2VvbV9zbW9vdGgoc2U9RkFMU0UsIGNvbG91cj0iZmlyZWJyaWNrIiwgbWV0aG9kID0gImxtIikgKwogIGdlb21fdGV4dChhZXMoeT0wLjc1LCB4PTAuNSwgbGFiZWw9Y29yX3JyX2xldmVsX3JyX3Nsb3BlKSwgY29sb3VyPSJmaXJlYnJpY2siKSAgKyAgCiAgI3NjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDYpKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudChoaWdoPSJsaWdodGJsdWUxIixsb3c9ImRhcmtibHVlIikgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkKCgoocGxvdF9zcGFjZXIoKSArIHBsb3Rfc3BhY2VyKCkgKyBjKSAvCiAgKHBsb3Rfc3BhY2VyKCkgKyBiICsgZikgLwogIChhICsgZCArIGUpCgpnZ3NhdmUoaGVyZSgiZmlndXJlcy9wdWxtb25hcnlfY29ycy5wZGYiKSwgd2lkdGg9OCwgaGVpZ2h0PTgpCgoKCmBgYAoKCiMjIyMgNy41IFdhcmQgbGV2ZWwgcHVsbW9uYXJ5IFRCIGVzdGltYXRlcwoKUGxvdCB0aGUgY291bnRlcmZhY3R1YWwgYXQgd2FyZCBsZXZlbAoKYGBge3J9CgpwbG90X2NvdW50ZXJmYWN0dWFsKG1vZGVsX2RhdGEgPSBtZGF0YSwgbW9kZWw9bV9wdWxtb25hcnksIG91dGNvbWUgPSBpbmNfMTAwaywgcG9wdWxhdGlvbl9kZW5vbWluYXRvciA9IHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAsIAogICAgICAgICAgICAgICAgICAgIGdyb3VwaW5nX3ZhciA9IHdhcmQsIHdhcmQsIHJlX2Zvcm11bGE9IH4oMSArIHlfbnVtKmFjZl9wZXJpb2QgfCB3YXJkKSkKICAKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvczMucG5nIiksIHdpZHRoPTEyLCBoZWlnaHQ9MTIpCgpgYGAKClN1bW1hcnkgb2YgY2hhbmdlIGluIG5vdGlmaWNhdGlvbnMgYXQgd2FyZCBsZXZlbAoKYGBge3J9Cgp3YXJkX2NoYW5nZSA8LSBzdW1tYXJpc2VfY2hhbmdlKG1vZGVsX2RhdGE9bWRhdGEsIG1vZGVsPW1fcHVsbW9uYXJ5LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yPXBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAsIGdyb3VwaW5nX3Zhcj13YXJkLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZV9mb3JtdWxhID0gfigxICsgeV9udW0qYWNmX3BlcmlvZCB8IHdhcmQpKQoKI3dhbnQgdG8ga2VlcCB0aGUgc3VtbWFyeSBlc3RpbWF0ZXMgaGVyZQp0b2tlZXAgPC0gYygicGVha19zdW1tYXJ5IiwgImxldmVsX3N1bW1hcnkiLCAic2xvcGVfc3VtbWFyeSIpCgojc3VtbWFyeSBtZWFzdXJlcyBpbiBhIHRhYmxlCndhcmRfY2hhbmdlICU+JQogIGtlZXAoKG5hbWVzKC4pICVpbiUgdG9rZWVwKSkgJT4lCiAgYmluZF9yb3dzKCkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKGVzdGltYXRlOi51cHBlciksIG51bWJlciwgYWNjdXJhY3k9MC4wMSkpICU+JQogIHNlbGVjdChtZWFzdXJlLCBldmVyeXRoaW5nKCkpICU+JQogIGRhdGF0YWJsZSgpCgoKI3Bsb3QgdGhlc2UgaW4gYSBmaWd1cmUKd2FyZF9lZmZlY3RzIDwtIHdhcmRfY2hhbmdlICU+JQogIGtlZXAoKG5hbWVzKC4pICVpbiUgdG9rZWVwKSkgJT4lCiAgYmluZF9yb3dzKCkgJT4lCiAgYmluZF9yb3dzKG92ZXJhbGxfY2hhbmdlJHBlYWtfc3VtbWFyeSkgJT4lCiAgYmluZF9yb3dzKG92ZXJhbGxfY2hhbmdlJGxldmVsX3N1bW1hcnkpICU+JQogIGJpbmRfcm93cyhvdmVyYWxsX2NoYW5nZSRzbG9wZV9zdW1tYXJ5KSAlPiUKICBtdXRhdGVfYXQoLnZhcnMgPSB2YXJzKGVzdGltYXRlOi51cHBlciksIAogICAgICAgICAgICAuZnVucyA9IGZ1bnMoYXMubnVtZXJpYykpICU+JQogIHNlbGVjdChtZWFzdXJlLCBldmVyeXRoaW5nKCkpICU+JQogIG11dGF0ZShlc3RpbWF0ZSA9IGFzLmRvdWJsZShlc3RpbWF0ZSkpICU+JQogIGZ1bGxfam9pbihnbGFzZ293X3dhcmRzXzE5NTEpICU+JSAKICBtdXRhdGUod2FyZDIgPSBwYXN0ZTAod2FyZF9udW1iZXIsICIuICIsIHdhcmQpKSAlPiUKICBtdXRhdGUod2FyZDIgPSBjYXNlX3doZW4oaXMubmEod2FyZCkgfiAiT3ZlcmFsbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IHdhcmQyKSkgJT4lCiAgc3RfYXNfc2YoKSAKCiNmdW5jdGlvbiBmb3IgcGxvdHRpbmcgY2hvcm9wbGV0aCBtYXBzCnBsb3Rfd2FyZF9lZmZlY3QgPC0gZnVuY3Rpb24oZGF0YSwgbWVhc3VyZSl7CiAge3tkYXRhfX0gJT4lCiAgZmlsdGVyKG1lYXN1cmUgPT0ge3ttZWFzdXJlfX0pICU+JQogIGdncGxvdCgpICsKICBnZW9tX3NmKGFlcyhmaWxsPWVzdGltYXRlKSkgKwogIGdlb21fc2ZfbGFiZWwoYWVzKGxhYmVsID0gd2FyZF9udW1iZXIpLCBzaXplPTMsIGZpbGw9TkEsIGxhYmVsLnNpemUgPSBOQSwgY29sb3VyPSJibGFjayIpICsKICBzY2FsZV9maWxsX2dyYWRpZW50KGhpZ2g9ImxpZ2h0Ymx1ZTEiLGxvdz0iZGFya2JsdWUiLCBuYW1lPSIiKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSksCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkgKwogICAgbGFicyh4PSIiLCB5PSIiKQp9CgojZnVuY3Rpb24gZm9yIHBsb3R0aW5nIGNhdGFwaWxsZXIgcGxvdHMKcGxvdF93YXJkX2NhdCA8LSBmdW5jdGlvbihkYXRhLCBtZWFzdXJlLCBzY2FsZSl7CgogIHBhbCA8LSBjb2xvclJhbXBQYWxldHRlKGMoJ2RhcmtibHVlJywnbGlnaHRibHVlJykpCgogIHt7ZGF0YX19ICU+JQogICAgZmlsdGVyKG1lYXN1cmU9PXt7bWVhc3VyZX19KSAlPiUKICAgIG11dGF0ZShteV9wYWxldHRlID0gY2FzZV93aGVuKHdhcmQyPT0iT3ZlcmFsbCIgfiAiI0M2MEMzMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IHBhbCgzNilbYXMubnVtZXJpYyhjdXQoLiRlc3RpbWF0ZSxicmVha3MgPSAzNikpXSkpICU+JQogICAgZ2dwbG90KCkgKwogICAgZ2VvbV9wb2ludHJhbmdlKGFlcyh5PWVzdGltYXRlLCB5bWluPS5sb3dlciwgeW1heD0udXBwZXIsIAogICAgICAgICAgICAgICAgICAgICAgeD1mY3RfcmVvcmRlcih3YXJkMiwgZXN0aW1hdGUpLCBjb2xvdXI9bXlfcGFsZXR0ZSkpICsKICAgIGNvb3JkX2ZsaXAoKSArCiAgICBzY2FsZV9jb2xvdXJfaWRlbnRpdHkobmFtZT0iIikgKwogICAgc2NhbGVfeV9jb250aW51b3VzKCkgKwogICAgdGhlbWVfZ2dkaXN0KCkgKwogICAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkgKwogICAgbGFicyh4ID0gIiIsCiAgICAgICAgIHkgPSAiUmVsYXRpdmUgcmF0ZSAoOTUlIENySSkiKQp9CgoKCndhcmRfcGVha19pIDwtIHBsb3Rfd2FyZF9lZmZlY3QoZGF0YSA9IHdhcmRfZWZmZWN0cywgbWVhc3VyZSA9ICJSUi5wZWFrIikKd2FyZF9sZXZlbF9pIDwtIHBsb3Rfd2FyZF9lZmZlY3QoZGF0YSA9IHdhcmRfZWZmZWN0cywgbWVhc3VyZSA9ICJSUi5sZXZlbCIpCndhcmRfc2xvcGVfaSA8LSBwbG90X3dhcmRfZWZmZWN0KGRhdGEgPSB3YXJkX2VmZmVjdHMsIG1lYXN1cmUgPSAiUlIuc2xvcGUiKQoKd2FyZF9wZWFrX2lpIDwtIHBsb3Rfd2FyZF9jYXQoZGF0YSA9IHdhcmRfZWZmZWN0cywgbWVhc3VyZSA9ICJSUi5wZWFrIikKd2FyZF9sZXZlbF9paSA8LSBwbG90X3dhcmRfY2F0KGRhdGEgPSB3YXJkX2VmZmVjdHMsIG1lYXN1cmUgPSAiUlIubGV2ZWwiKQp3YXJkX3Nsb3BlX2lpIDwtIHBsb3Rfd2FyZF9jYXQoZGF0YSA9IHdhcmRfZWZmZWN0cywgbWVhc3VyZSA9ICJSUi5zbG9wZSIpCgpzNCA8LSAod2FyZF9wZWFrX2kgKyB3YXJkX2xldmVsX2kgKyB3YXJkX3Nsb3BlX2kpIC8KICAod2FyZF9wZWFrX2lpICsgd2FyZF9sZXZlbF9paSArIHdhcmRfc2xvcGVfaWkpCgpzNFtbMV1dIDwtIHM0W1sxXV0gKyBwbG90X2xheW91dCh0YWdfbGV2ZWwgPSAnbmV3JykKczRbWzJdXSA8LSBzNFtbMl1dICsgcGxvdF9sYXlvdXQodGFnX2xldmVsID0gJ25ldycpCnM0ICsgcGxvdF9hbm5vdGF0aW9uKHRhZ19sZXZlbHMgPSBjKCdBJywgJzEnKSkKCgpnZ3NhdmUoaGVyZSgiZmlndXJlcy9zNC5wbmciKSwgd2lkdGggPSAxNiwgaGVpZ2h0PTEwKQoKYGBgCgpDYWxjdWxhdGUgdGhlIGNvdW50ZXJmYWN0dWFsIHBlciB3YXJkCgpgYGB7cn0KCndhcmRfcHVsbW9uYXJ5X2NvdW50ZXJmIDwtIGNhbGN1bGF0ZV9jb3VudGVyZmFjdHVhbChtb2RlbF9kYXRhID0gbWRhdGEsIG1vZGVsPW1fcHVsbW9uYXJ5LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvcHVsYXRpb25fZGVub21pbmF0b3IgPSBwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBpbmdfdmFyID0gd2FyZCwgcmVfZm9ybXVsYT1+KDEgKyB5X251bSphY2ZfcGVyaW9kIHwgd2FyZCkpCgp3YXJkX3B1bG1vbmFyeV9jb3VudGVyZiRjb3VudGVyX3Bvc3QgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKGNhc2VzX2F2ZXJ0ZWQ6Y2FzZXNfYXZlcnRlZC51cHBlciwgZGlmZl9pbmMxMDBrOmRpZmZfaW5jMTAway51cHBlciksIG51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjEsIGJpZy5tYXJrID0gIiwiKSkpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhycl9pbmMxMDBrOnJyX2luYzEwMGsudXBwZXIpLCBudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4wMSkpKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMocGN0X2NoYW5nZTpwY3RfY2hhbmdlLnVwcGVyKSwgcGVyY2VudCwgYWNjdXJhY3k9MC4xKSkgJT4lCiAgZGF0YXRhYmxlKCkKCgpgYGAKCk92ZXJhbGwgY291bnRlcmZhY3R1YWwgcGVyIHdhcmQKCmBgYHtyfQoKd2FyZF9wdWxtb25hcnlfY291bnRlcmYkY291bnRlcl9wb3N0X292ZXJhbGwgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKGNhc2VzX2F2ZXJ0ZWQ6Y2FzZXNfYXZlcnRlZC51cHBlciksIG51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjEsIGJpZy5tYXJrID0gIiwiKSkpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhwY3RfY2hhbmdlOnBjdF9jaGFuZ2UudXBwZXIpLCBwZXJjZW50LCBhY2N1cmFjeT0wLjEpKSAlPiUKICBkYXRhdGFibGUoKQoKYGBgCgoKCiMjIyA4LiBFeHRyYS1wdWxtb25hcnkgVEIgbm90aWZpY2F0aW9ucwoKTm93IHdlIHdpbGwgbW9kZWwgdGhlIGV4dHJhLXB1bG1vbmFyeSBUQiBub3RpZmljYXRpb24gcmF0ZS4gU3RydWdnbGluZyBhIGJpdCB3aXRoIG5lZ2F0aXZlIGJpbm9taWFsIG1vZGVsLCBzbyByZXZlcnQgdG8gUG9pc3Nvbi4KCiMjIyMgOC4xIEZpdCB0aGUgbW9kZWwKCmBgYHtyfQoKbV9leHRyYXB1bG1vbmFyeSA8LSBicm0oCiAgY2FzZXMgfiAxICsgeV9udW0qYWNmX3BlcmlvZCArICgxICsgeV9udW0qYWNmX3BlcmlvZCB8IHdhcmQpICsgb2Zmc2V0KGxvZyhwb3B1bGF0aW9uX3dpdGhvdXRfaW5zdF9zaGlwKSksCiAgICAgICAgICAgICAgICAgIGRhdGEgPSBtZGF0YV9leHRyYXB1bG1vbmFyeSwKICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gbmVnYmlub21pYWwoKSwKICAgICAgICAgICAgICAgICAgc2VlZCA9IDEyMzQsCiAgICAgICAgICAgICAgICAgIGNoYWlucyA9IDQsIGNvcmVzID0gNCwKICAgICAgICAgICAgICAgICAgcHJpb3IgPSBwcmlvcihub3JtYWwoMCwxMDAwKSwgY2xhc3MgPSBJbnRlcmNlcHQpICsKICAgICAgICAgICAgICAgICAgICAgICAgICBwcmlvcihnYW1tYSgwLjAxLCAwLjAxKSwgY2xhc3MgPSBzaGFwZSkgKwogICAgICAgICAgICAgICAgICAgICAgICAgIHByaW9yKG5vcm1hbCgwLCAxKSwgY2xhc3MgPSBiKSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpb3IoZXhwb25lbnRpYWwoMSksIGNsYXNzPXNkKSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpb3IobGtqKDIpLCBjbGFzcz1jb3IpKQoKc3VtbWFyeShtX2V4dHJhcHVsbW9uYXJ5KQpwbG90KG1fZXh0cmFwdWxtb25hcnkpCnBwX2NoZWNrKG1fZXh0cmFwdWxtb25hcnksIHR5cGU9J2VjZGZfb3ZlcmxheScpCgpgYGAKCiMjIyMgOC4yIFN1bW1hcnkgb2YgY2hhbmdlCgpTdW1tYXJpc2UgaW4gcGxvdAoKYGBge3J9CnBsb3RfY291bnRlcmZhY3R1YWwobW9kZWxfZGF0YSA9IG92ZXJhbGxfc2NhZmZvbGQgJT4lIGZpbHRlcih5ZWFyPD0xOTYxKSwgbW9kZWw9bV9leHRyYXB1bG1vbmFyeSwgCiAgICAgICAgICAgICAgICAgICAgcG9wdWxhdGlvbl9kZW5vbWluYXRvciA9IHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAsIG91dGNvbWU9aW5jXzEwMGtfZXh0cmFwdWxtb25hcnksIHJlX2Zvcm11bGEgPSBOQSkKICAKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvczYucG5nIiksIHdpZHRoPTEwKQoKYGBgCgpTdW1tYXJpc2UgbnVtZXJpY2FsbHkuCgpgYGB7cn0KCm92ZXJhbGxfY2hhbmdlX2V4dHJhcHVsbW9uYXJ5IDwtIHN1bW1hcmlzZV9jaGFuZ2UobW9kZWxfZGF0YT1vdmVyYWxsX3NjYWZmb2xkLCBtb2RlbD1tX2V4dHJhcHVsbW9uYXJ5LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yPXBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXAsIGdyb3VwaW5nX3Zhcj1OVUxMLCByZV9mb3JtdWxhID0gTkEpCgojd2FudCB0byBrZWVwIHRoZSBzdW1tYXJ5IGVzdGltYXRlcyBoZXJlCnRva2VlcCA8LSBjKCJwZWFrX3N1bW1hcnkiLCAibGV2ZWxfc3VtbWFyeSIsICJzbG9wZV9zdW1tYXJ5IikKCiNzdW1tYXJ5IG1lYXN1cmVzIGluIGEgdGFibGUKb3ZlcmFsbF9jaGFuZ2VfZXh0cmFwdWxtb25hcnkgJT4lCiAga2VlcChuYW1lcyguKSAlaW4lIHRva2VlcCkgJT4lCiAgYmluZF9yb3dzKCkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKGVzdGltYXRlOi51cHBlciksIG51bWJlciwgYWNjdXJhY3k9MC4wMSkpICU+JQogIHNlbGVjdChtZWFzdXJlLCBldmVyeXRoaW5nKCkpICU+JQogIGRhdGF0YWJsZSgpCgpgYGAKCiMjIyMgOC4zIENvbXBhcmVkIHRvIGNvdW50ZXJmYWN0dWFsCgpOdW1iZXJzIG9mIGV4dHJhLXB1bG1vbmFyeSBUQiBjYXNlcyBhdmVydGVkIG92ZXJhbGwuCgpgYGB7cn0KCm92ZXJhbGxfZXBfY291bnRlcmYgPC0gY2FsY3VsYXRlX2NvdW50ZXJmYWN0dWFsKG1vZGVsX2RhdGEgPSBtZGF0YV9leHRyYXB1bG1vbmFyeSwgbW9kZWw9bV9leHRyYXB1bG1vbmFyeSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9wdWxhdGlvbl9kZW5vbWluYXRvciA9IHBvcHVsYXRpb25fd2l0aG91dF9pbnN0X3NoaXApCgpvdmVyYWxsX2VwX2NvdW50ZXJmJGNvdW50ZXJfcG9zdCAlPiUKICBtdXRhdGUoYWNyb3NzKGMoY2FzZXNfYXZlcnRlZDpjYXNlc19hdmVydGVkLnVwcGVyLCBkaWZmX2luYzEwMGs6ZGlmZl9pbmMxMDBrLnVwcGVyKSwgbnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDAuMSwgYmlnLm1hcmsgPSAiLCIpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKHJyX2luYzEwMGs6cnJfaW5jMTAway51cHBlciksIG51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjAxKSkpICU+JQogIG11dGF0ZShhY3Jvc3MoYyhwY3RfY2hhbmdlOnBjdF9jaGFuZ2UudXBwZXIpLCBwZXJjZW50LCBhY2N1cmFjeT0wLjEpKSAlPiUKICBkYXRhdGFibGUoKQoKYGBgCgpUb3RhbCBleHRyYXB1bG1vbmFyeSBUQiBjYXNlcyBhdmVydGVkIGJldHdlZW4gMTk1OCBhbmQgMTk2MwoKYGBge3J9CgpvdmVyYWxsX2VwX2NvdW50ZXJmJGNvdW50ZXJfcG9zdF9vdmVyYWxsICU+JQogIG11dGF0ZShhY3Jvc3MoYyhjYXNlc19hdmVydGVkOmNhc2VzX2F2ZXJ0ZWQudXBwZXIpLCBudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4xLCBiaWcubWFyayA9ICIsIikpKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMocGN0X2NoYW5nZTpwY3RfY2hhbmdlLnVwcGVyKSwgcGVyY2VudCwgYWNjdXJhY3k9MC4xKSkgJT4lCiAgZGF0YXRhYmxlKCkKCgpgYGAKCiMjIyMgOC40IFdhcmQtbGV2ZWwgZXh0cmEtcHVsbW9uYXJ5IHN1bW1hcmllcwoKV2FyZC1sZXZlbCBleHRyYS1wdWxtb25hcnkgZXN0aW1hdGVzIGluIGdyYXBoaWNhbCBmb3JtLgoKYGBge3J9CgpwbG90X2NvdW50ZXJmYWN0dWFsKG1vZGVsX2RhdGEgPSBtZGF0YV9leHRyYXB1bG1vbmFyeSwgbW9kZWw9bV9leHRyYXB1bG1vbmFyeSwgb3V0Y29tZSA9IGluY18xMDBrLCAKICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yID0gcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgZ3JvdXBpbmdfdmFyID0gd2FyZCxyZV9mb3JtdWxhID1+KHlfbnVtKmFjZl9wZXJpb2QgfCB3YXJkKSwgCiAgICAgICAgICAgICAgICAgICAgd2FyZCkKICAKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvczQucG5nIiksIHdpZHRoPTEwLCBoZWlnaHQ9MTIpCgpgYGAKCk51bWVyaWNhbCBzdW1tYXJ5LgoKYGBge3J9Cgp3YXJkX2NoYW5nZV9leHRyYXB1bG1vbmFyeSA8LSBzdW1tYXJpc2VfY2hhbmdlKG1vZGVsX2RhdGEgPSBtZGF0YV9leHRyYXB1bG1vbmFyeSwgbW9kZWwgPSBtX2V4dHJhcHVsbW9uYXJ5LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uX2Rlbm9taW5hdG9yID0gcG9wdWxhdGlvbl93aXRob3V0X2luc3Rfc2hpcCwgZ3JvdXBpbmdfdmFyPXdhcmQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVfZm9ybXVsYSA9IH4oeV9udW0qYWNmX3BlcmlvZCB8IHdhcmQpKSAKCiN3YW50IHRvIGtlZXAgdGhlIHN1bW1hcnkgZXN0aW1hdGVzIGhlcmUKdG9rZWVwIDwtIGMoInBlYWtfc3VtbWFyeSIsICJsZXZlbF9zdW1tYXJ5IiwgInNsb3BlX3N1bW1hcnkiKQoKI3N1bW1hcnkgbWVhc3VyZXMgaW4gYSB0YWJsZQp3YXJkX2NoYW5nZV9leHRyYXB1bG1vbmFyeSAgJT4lCiAga2VlcChuYW1lcyguKSAlaW4lIHRva2VlcCkgJT4lCiAgYmluZF9yb3dzKCkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKGVzdGltYXRlOi51cHBlciksIG51bWJlciwgYWNjdXJhY3k9MC4wMSkpICU+JQogIHNlbGVjdChtZWFzdXJlLCBldmVyeXRoaW5nKCkpICU+JQogIGRhdGF0YWJsZSgpCgoKCmBgYAoKCiMjIyA5LiBBZ2Utc2V4IG1vZGVsCgojIyMjIDkuMSBGSXQgdGhlIG1vZGVsCgpGaXQgdGhlIG1vZGVsCgooTm90IHJld3JpdHRlbiB0aGUgZnVuY3Rpb25zIGZvciB0aGlzIHlldCkKCmBgYHtyfQoKbWRhdGFfYWdlX3NleCA8LSBjYXNlc19ieV9hZ2Vfc2V4ICU+JQogIGZpbHRlcih0Yl90eXBlPT0iUHVsbW9uYXJ5IikgJT4lCiAgbXV0YXRlKGFjZl9wZXJpb2QgPSBjYXNlX3doZW4oeWVhciAlaW4lIGMoMTk1MDoxOTU2KSB+ICJhLiBwcmUtYWNmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyICVpbiUgYygxOTU3KSB+ICJiLiBhY2YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgJWluJSBjKDE5NTg6MTk2MykgfiAiYy4gcG9zdC1hY2YiKSkgJT4lCiAgbXV0YXRlKHllYXIyID0geWVhciswLjUpICU+JQogIGdyb3VwX2J5KGFnZSwgc2V4KSAlPiUKICBtdXRhdGUoeV9udW0gPSByb3dfbnVtYmVyKCkpICU+JQogIHVuZ3JvdXAoKQoKbV9hZ2Vfc2V4IDwtIGJybSgKICBjYXNlcyB+IHlfbnVtICsgKGFjZl9wZXJpb2QpKihhZ2Uqc2V4KSArIChhY2ZfcGVyaW9kOnlfbnVtKSooYWdlKnNleCksCiAgICAgICAgICAgICAgICAgIGRhdGEgPSBtZGF0YV9hZ2Vfc2V4LAogICAgICAgICAgICAgICAgICBmYW1pbHkgPSBuZWdiaW5vbWlhbCgpLAogICAgICAgICAgICAgICAgICBzZWVkID0gMTIzNCwKICAgICAgICAgICAgICAgICAgY2hhaW5zID0gNCwgY29yZXMgPSA0LCAKICAgICAgICAgICAgICAgICAgcHJpb3IgPSBwcmlvcihub3JtYWwoMCwxKSwgY2xhc3MgPSBJbnRlcmNlcHQpICsKICAgICAgICAgICAgICAgICAgICAgICAgICBwcmlvcihnYW1tYSgwLjAxLCAwLjAxKSwgY2xhc3MgPSBzaGFwZSkgKwogICAgICAgICAgICAgICAgICAgICAgICAgIHByaW9yKG5vcm1hbCgwLCAxKSwgY2xhc3MgPSBiKSkKCnN1bW1hcnkobV9hZ2Vfc2V4KQpwbG90KG1fYWdlX3NleCkKcHBfY2hlY2sobV9hZ2Vfc2V4LCB0eXBlPSdlY2RmX292ZXJsYXknKQpjb25kaXRpb25hbF9lZmZlY3RzKG1fYWdlX3NleCkKCmBgYAoKU3VtbWFyaXNlIHBvc3RlcmlvcgoKCmBgYHtyfQoKI3Bvc3RlcmlvciBkcmF3cywgYW5kIHN1bW1hcmlzZQphZ2Vfc2V4X3N1bW1hcnkgPC0gbWRhdGFfYWdlX3NleCAlPiUKICBzZWxlY3QoeWVhciwgeWVhcjIsIHlfbnVtLCBhY2ZfcGVyaW9kLCBhZ2UsIHNleCkgJT4lCiAgYWRkX2VwcmVkX2RyYXdzKG1fYWdlX3NleCkgJT4lCiAgZ3JvdXBfYnkoeWVhcjIsIGFjZl9wZXJpb2QsIGFnZSwgc2V4KSAlPiUKICBtZWFuX3FpKCkgJT4lCiAgbXV0YXRlKGFjZl9wZXJpb2QgPSBjYXNlX3doZW4oYWNmX3BlcmlvZD09ImEuIHByZS1hY2YiIH4gIkJlZm9yZSBJbnRlcnZlbnRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2Q9PSJjLiBwb3N0LWFjZiIgfiAiUG9zdCBJbnRlcnZlbnRpb24iKSkKCiNjcmVhdGUgdGhlIGNvdW50ZXJmYWN0dWFsIChubyBpbnRlcnZlbnRpb24pLCBhbmQgc3VtbWFyaXNlCmFnZV9zZXhfY291bnRlcmZhY3QgPC0gCiAgdGliYmxlKHllYXIgPSBtZGF0YV9hZ2Vfc2V4JHllYXIsCiAgICAgICAgIHllYXIyID0gbWRhdGFfYWdlX3NleCR5ZWFyMiwKICAgICAgICAgeV9udW0gPSBtZGF0YV9hZ2Vfc2V4JHlfbnVtLAogICAgICAgICBhZ2UgPSBtZGF0YV9hZ2Vfc2V4JGFnZSwKICAgICAgICAgc2V4ID0gbWRhdGFfYWdlX3NleCRzZXgsCiAgICAgICAgIGFjZl9wZXJpb2QgPSBmYWN0b3IoImEuIHByZS1hY2YiKSkgJT4lCiAgYWRkX2VwcmVkX2RyYXdzKG1fYWdlX3NleCkgJT4lCiAgZ3JvdXBfYnkoeWVhcjIsIGFjZl9wZXJpb2QsIGFnZSwgc2V4KSAlPiUKICBtZWFuX3FpKCkgJT4lCiAgbXV0YXRlKGFjZl9wZXJpb2QgPSBjYXNlX3doZW4oYWNmX3BlcmlvZD09ImEuIHByZS1hY2YiIH4gIkJlZm9yZSBJbnRlcnZlbnRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2Q9PSJjLiBwb3N0LWFjZiIgfiAiUG9zdCBJbnRlcnZlbnRpb24iKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShhZ2UgPSBjYXNlX3doZW4oYWdlPT0iMDBfMDUiIH4gIjAgdG8gNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMDZfMTUiIH4gIjA2IHRvIDE1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIxNl8yNSIgfiAiMTYgdG8gMjV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjI2XzM1IiB+ICIyNiB0byAzNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMzZfNDUiIH4gIjM2IHRvIDQ1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI0Nl81NSIgfiAiNDYgdG8gNTV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjU2XzY1IiB+ICI1NiB0byA2NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNjUrIiB+ICI2NSAmIHVwIHkiKSkgJT4lCiAgbXV0YXRlKHNleCA9IGNhc2Vfd2hlbihzZXg9PSAiTSIgfiAiTWFsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBzZXg9PSAiRiIgfiAiRmVtYWxlIikpIAoKCgphZ2Vfc2V4X3N1bW1hcnkgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShhZ2UgPSBjYXNlX3doZW4oYWdlPT0iMDBfMDUiIH4gIjAgdG8gNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMDZfMTUiIH4gIjA2IHRvIDE1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIxNl8yNSIgfiAiMTYgdG8gMjV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjI2XzM1IiB+ICIyNiB0byAzNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMzZfNDUiIH4gIjM2IHRvIDQ1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI0Nl81NSIgfiAiNDYgdG8gNTV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjU2XzY1IiB+ICI1NiB0byA2NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNjUrIiB+ICI2NSAmIHVwIHkiKSkgJT4lCiAgbXV0YXRlKHNleCA9IGNhc2Vfd2hlbihzZXg9PSAiTSIgfiAiTWFsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBzZXg9PSAiRiIgfiAiRmVtYWxlIikpICU+JQogIGdncGxvdCgpICsKICBnZW9tX3JpYmJvbihhZXMoeW1pbj0uZXByZWQubG93ZXIsIHltYXg9LmVwcmVkLnVwcGVyLCB4PXllYXIyLCBncm91cCA9IGFjZl9wZXJpb2QsIGZpbGw9YWNmX3BlcmlvZCksIGFscGhhPTAuNSkgKwogIGdlb21fcmliYm9uKGRhdGEgPSBhZ2Vfc2V4X2NvdW50ZXJmYWN0ICU+JSBmaWx0ZXIoeWVhcj49MTk1NiksIAogICAgICAgICAgICAgIGFlcyh5bWluPS5lcHJlZC5sb3dlciwgeW1heD0uZXByZWQudXBwZXIsIHg9eWVhcjIsIGZpbGw9IkNvdW50ZXJmYWN0dWFsIiksIGFscGhhPTAuNSkgKwogIGdlb21fbGluZShkYXRhID0gYWdlX3NleF9jb3VudGVyZmFjdCAlPiUgZmlsdGVyKHllYXI+PTE5NTYpLCAKICAgICAgICAgICAgICBhZXMoeT0uZXByZWQsIHg9eWVhcjIsIGNvbG91cj0iQ291bnRlcmZhY3R1YWwiKSkgKwogIGdlb21fbGluZShhZXMoeT0uZXByZWQsIHg9eWVhcjIsIGdyb3VwPWFjZl9wZXJpb2QsICBjb2xvdXI9YWNmX3BlcmlvZCkpICsKICBnZW9tX3BvaW50KGRhdGEgPSBtZGF0YV9hZ2Vfc2V4ICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUoYWdlID0gY2FzZV93aGVuKGFnZT09IjAwXzA1IiB+ICIwIHRvIDV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjA2XzE1IiB+ICIwNiB0byAxNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMTZfMjUiIH4gIjE2IHRvIDI1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIyNl8zNSIgfiAiMjYgdG8gMzV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjM2XzQ1IiB+ICIzNiB0byA0NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNDZfNTUiIH4gIjQ2IHRvIDU1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI1Nl82NSIgfiAiNTYgdG8gNjV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjY1KyIgfiAiNjUgJiB1cCB5IikpICU+JQogIG11dGF0ZShzZXggPSBjYXNlX3doZW4oc2V4PT0gIk0iIH4gIk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgc2V4PT0gIkYiIH4gIkZlbWFsZSIpKSAlPiUKICBtdXRhdGUoYWNmX3BlcmlvZCA9IGNhc2Vfd2hlbihhY2ZfcGVyaW9kPT0iYS4gcHJlLWFjZiIgfiAiQmVmb3JlIEludGVydmVudGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWNmX3BlcmlvZD09ImIuIGFjZiIgfiAiQ291bnRlcmZhY3R1YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWNmX3BlcmlvZD09ImMuIHBvc3QtYWNmIiB+ICJQb3N0IEludGVydmVudGlvbiIpKSwgCiAgYWVzKHk9Y2FzZXMsIHg9eWVhcjIsIHNoYXBlPWFjZl9wZXJpb2QpLCBzaXplPTIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWFjZl9lbmQpLCBsaW5ldHlwZT0zKSArCiAgZ2doNHg6OmZhY2V0X2dyaWQyKGFnZX5zZXgsIHNjYWxlcyA9ICJmcmVlX3kiLCBpbmRlcGVuZGVudCA9ICJ5IikgKwogIHRoZW1lX2dnZGlzdCgpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHllYXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSB5ZWFyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9heGlzKGFuZ2xlID0gOTApKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiI0RFMEQ5MiIsICJncmV5NTAiLCAiIzRENkNGQSIpICwgbmFtZT0iIikgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiI0RFMEQ5MiIsICJncmV5NTAiLCAiIzRENkNGQSIpICwgbmFtZT0iIikgKwogIHNjYWxlX3NoYXBlKG5hbWU9IiIpICsKICBsYWJzKAogICAgeCA9ICJZZWFyIiwKICAgIHkgPSAiQ2FzZSBub3RpZmljYXRpb25zIChuKSIsCiAgICBjYXB0aW9uID0gIk1hc3MgbWluaWF0dXJlIFgtcmF5IGNhbXBhaWduIHBlcmlvZCBiZXR3ZWVuIGRhc2hlZCBsaW5lcyAoMTF0aCBNYXJjaC0xMnRoIEFwcmlsIDE5NTcpIgogICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSksCiAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTIpKQogIApnZ3NhdmUoaGVyZSgiZmlndXJlcy9zNy5wbmciKSwgaGVpZ2h0PTEwKQoKYGBgCgojIyMjIDkuMiBTdW1tYXJ5IG9mIGltcGFjdCBvZiBpbnRlcnZlbnRpb24KCkNhbGN1bGF0ZSBzdW1tYXJ5IGVmZmVjdHMKCmBgYHtyfQoKI1BlYWsKb3V0X2FnZV9zZXhfMSA8LSBjcm9zc2luZyhtZGF0YV9hZ2Vfc2V4ICU+JSAKICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCh5X251bSwgYWdlLCBzZXgpICU+JQogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHlfbnVtID09IDgpLAogICAgICAgICAgICAgICAgICAgICAgYWNmX3BlcmlvZCA9IGMoImEuIHByZS1hY2YiLCAiYi4gYWNmIikpCgpwZWFrX2RyYXdzX2FnZV9zZXggPC0gYWRkX2VwcmVkX2RyYXdzKG5ld2RhdGEgPSBvdXRfYWdlX3NleF8xLAogICAgICAgICAgICAgICAgICBvYmplY3QgPSBtX2FnZV9zZXgpICU+JQogICAgZ3JvdXBfYnkoLmRyYXcsIGFnZSwgc2V4KSAlPiUKICAgIHN1bW1hcmlzZShlc3RpbWF0ZSA9IGxhc3QoLmVwcmVkKS9maXJzdCguZXByZWQpKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLnBlYWsiKQogIApwZWFrX3N1bW1hcnlfYWdlX3NleCA8LSBwZWFrX2RyYXdzX2FnZV9zZXggJT4lCiAgICBncm91cF9ieShhZ2UsIHNleCkgJT4lCiAgICBtZWFuX3FpKGVzdGltYXRlKSAlPiUKICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLnBlYWsiKQoKCiNMZXZlbAogCm91dF9hZ2Vfc2V4XzIgPC0gY3Jvc3NpbmcobWRhdGFfYWdlX3NleCAlPiUgCiAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoeV9udW0sIGFnZSwgc2V4KSAlPiUKICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcih5X251bSA9PSA5KSwKICAgICAgICAgICAgICAgICAgICAgIGFjZl9wZXJpb2QgPSBjKCJhLiBwcmUtYWNmIiwgImMuIHBvc3QtYWNmIikpCiAgCmxldmVsX2RyYXdzX2FnZV9zZXggPC0gYWRkX2VwcmVkX2RyYXdzKG5ld2RhdGEgPSBvdXRfYWdlX3NleF8yLAogICAgICAgICAgICAgICAgICBvYmplY3QgPSBtX2FnZV9zZXgpICU+JQogICAgYXJyYW5nZSh5X251bSwgLmRyYXcpICU+JQogICAgZ3JvdXBfYnkoLmRyYXcsIGFnZSwgc2V4KSAlPiUKICAgIHN1bW1hcmlzZShlc3RpbWF0ZSA9IGxhc3QoLmVwcmVkKS9maXJzdCguZXByZWQpKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLmxldmVsIikKICAKbGV2ZWxfc3VtbWFyeV9hZ2Vfc2V4IDwtIGxldmVsX2RyYXdzX2FnZV9zZXggJT4lCiAgICBncm91cF9ieShhZ2UsIHNleCkgJT4lCiAgICBtZWFuX3FpKGVzdGltYXRlKSAlPiUKICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLmxldmVsIikKCiNTbG9wZQoKb3V0X2FnZV9zZXhfMyA8LSBjcm9zc2luZyhtZGF0YV9hZ2Vfc2V4ICU+JSAKICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCh5X251bSwgYWdlLCBzZXgpICU+JQogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHlfbnVtICVpbiUgYyg5LDE0KSksCiAgICAgICAgICAgICAgICAgICAgYWNmX3BlcmlvZCA9IGMoImEuIHByZS1hY2YiLCAiYy4gcG9zdC1hY2YiKSkKICAKc2xvcGVfZHJhd3NfYWdlX3NleCA8LSBhZGRfZXByZWRfZHJhd3MobmV3ZGF0YSA9IG91dF9hZ2Vfc2V4XzMsCiAgICAgICAgICAgICAgICAgIG9iamVjdCA9IG1fYWdlX3NleCkgJT4lCiAgICAgICAgYXJyYW5nZSh5X251bSkgJT4lCiAgICAgICAgdW5ncm91cCgpICU+JQogICAgICAgIGdyb3VwX2J5KC5kcmF3LCBhY2ZfcGVyaW9kLCBhZ2UsIHNleCkgJT4lCiAgICAgICAgc3VtbWFyaXNlKHNsb3BlID0gKGxhc3QoLmVwcmVkKSAtIGZpcnN0KC5lcHJlZCkpIC8gKGxhc3QoeV9udW0pLWZpcnN0KHlfbnVtKSkpICU+JQogICAgICAgIHVuZ3JvdXAoKSAlPiUKICAgICAgICBncm91cF9ieSguZHJhdywgYWdlLCBzZXgpICU+JQogICAgICAgIHN1bW1hcmlzZShlc3RpbWF0ZSA9IGxhc3Qoc2xvcGUpL2ZpcnN0KHNsb3BlKSkgJT4lCiAgICAgICAgbXV0YXRlKG1lYXN1cmUgPSAiUlIuc2xvcGUiKQogIApzbG9wZV9zdW1tYXJ5X2FnZV9zZXggPC0gc2xvcGVfZHJhd3NfYWdlX3NleCAlPiUKICAgICBncm91cF9ieShhZ2UsIHNleCkgJT4lCiAgICAgIG1lZGlhbl9xaShlc3RpbWF0ZSkgJT4lCiAgICAgIG11dGF0ZShtZWFzdXJlID0gIlJSLnNsb3BlIikKCmBgYAoKTnVtZXJpY2Egc3VtYW1yeSBvZiB0aGVzZSBzdW1tYXJ5IHJlc3VsdHMKCmBgYHtyfQoKYmluZF9yb3dzKAogIHBlYWtfc3VtbWFyeV9hZ2Vfc2V4LCBsZXZlbF9zdW1tYXJ5X2FnZV9zZXgsIHNsb3BlX3N1bW1hcnlfYWdlX3NleAopICU+JQogIG11dGF0ZShhY3Jvc3MoYyhlc3RpbWF0ZToudXBwZXIpLCBudW1iZXIsIGFjY3VyYWN5PTAuMDEpKSAlPiUKICBzZWxlY3QobWVhc3VyZSwgZXZlcnl0aGluZygpKSAlPiUKICBkYXRhdGFibGUoKQoKCgpgYGAKCkFzIGEgZmlndXJlCgpgYGB7cn0KCnBlYWtfZ19hZ2Vfc2V4IDwtIHBlYWtfc3VtbWFyeV9hZ2Vfc2V4ICU+JQogIG11dGF0ZShzZXggPSBjYXNlX3doZW4oc2V4PT0iTSIgfiAiTWFsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBzZXg9PSJGIiB+ICJGZW1hbGUiKSkgJT4lCiAgbXV0YXRlKGFnZSA9IGNhc2Vfd2hlbihhZ2U9PSIwMF8wNSIgfiAiMCB0byA1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIwNl8xNSIgfiAiMDYgdG8gMTV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjE2XzI1IiB+ICIxNiB0byAyNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMjZfMzUiIH4gIjI2IHRvIDM1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIzNl80NSIgfiAiMzYgdG8gNDV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjQ2XzU1IiB+ICI0NiB0byA1NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNTZfNjUiIH4gIjU2IHRvIDY1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI2NSsiIH4gIjY1ICYgdXAgeSIpKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdD0xKSwgbGluZXR5cGU9MikrCiAgZ2VvbV9wb2ludHJhbmdlKGFlcyh4PWFnZSwgeT1lc3RpbWF0ZSwgeW1pbj0ubG93ZXIsIHltYXg9LnVwcGVyLCBncm91cD1zZXgsIGNvbG91cj1zZXgsIHNoYXBlPXNleCksCiAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjUpKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKCIjQ0Q3QUM1IiwgImNhZGV0Ymx1ZTMiKSwgbmFtZT0iIikgKwogIHNjYWxlX3NoYXBlKG5hbWU9IiIpICsKICBsYWJzKHg9IiIsCiAgICAgICB5PSJSZWxhdGl2ZSByYXRlICg5NSUgVUkpIikgKwogIHRoZW1lX2dnZGlzdCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXk3OCIsIGZpbGw9TkEpKQoKI2xldmVsIHBsb3QKbGV2ZWxfZ19hZ2Vfc2V4IDwtIGxldmVsX3N1bW1hcnlfYWdlX3NleCAlPiUKICBtdXRhdGUoc2V4ID0gY2FzZV93aGVuKHNleD09Ik0iIH4gIk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgc2V4PT0iRiIgfiAiRmVtYWxlIikpICU+JQogIG11dGF0ZShhZ2UgPSBjYXNlX3doZW4oYWdlPT0iMDBfMDUiIH4gIjAgdG8gNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMDZfMTUiIH4gIjA2IHRvIDE1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIxNl8yNSIgfiAiMTYgdG8gMjV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjI2XzM1IiB+ICIyNiB0byAzNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMzZfNDUiIH4gIjM2IHRvIDQ1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI0Nl81NSIgfiAiNDYgdG8gNTV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjU2XzY1IiB+ICI1NiB0byA2NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNjUrIiB+ICI2NSAmIHVwIHkiKSkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQ9MSksIGxpbmV0eXBlPTIpKwogIGdlb21fcG9pbnRyYW5nZShhZXMoeD1hZ2UsIHk9ZXN0aW1hdGUsIHltaW49Lmxvd2VyLCB5bWF4PS51cHBlciwgZ3JvdXA9c2V4LCBjb2xvdXI9c2V4LCBzaGFwZT1zZXgpLAogICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC41KSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiI0NEN0FDNSIsICJjYWRldGJsdWUzIiksIG5hbWU9IiIpICsKICBzY2FsZV9zaGFwZShuYW1lPSIiKSArCiAgbGFicyh4PSIiLAogICAgICAgeT0iUmVsYXRpdmUgcmF0ZSAoOTUlIFVJKSIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSkKCiNzbG9wZSBwbG90CnNsb3BlX2dfYWdlX3NleCA8LSBzbG9wZV9zdW1tYXJ5X2FnZV9zZXggJT4lCiAgbXV0YXRlKHNleCA9IGNhc2Vfd2hlbihzZXg9PSJNIiB+ICJNYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgIHNleD09IkYiIH4gIkZlbWFsZSIpKSAlPiUKICBtdXRhdGUoYWdlID0gY2FzZV93aGVuKGFnZT09IjAwXzA1IiB+ICIwIHRvIDV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjA2XzE1IiB+ICIwNiB0byAxNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMTZfMjUiIH4gIjE2IHRvIDI1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIyNl8zNSIgfiAiMjYgdG8gMzV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjM2XzQ1IiB+ICIzNiB0byA0NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNDZfNTUiIH4gIjQ2IHRvIDU1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI1Nl82NSIgfiAiNTYgdG8gNjV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjY1KyIgfiAiNjUgJiB1cCB5IikpICU+JQogIGdncGxvdCgpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PTEpLCBsaW5ldHlwZT0yKSsKICBnZW9tX3BvaW50cmFuZ2UoYWVzKHg9YWdlLCB5PWVzdGltYXRlLCB5bWluPS5sb3dlciwgeW1heD0udXBwZXIsIGdyb3VwPXNleCwgY29sb3VyPXNleCwgc2hhcGU9c2V4KSwKICAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuNSkpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoIiNDRDdBQzUiLCAiY2FkZXRibHVlMyIpLCBuYW1lPSIiKSArCiAgc2NhbGVfc2hhcGUobmFtZT0iIikgKwogIGxhYnMoeD0iIiwKICAgICAgIHk9IlJlbGF0aXZlIHJhdGUgKDk1JSBVSSkiKSArCiAgdGhlbWVfZ2dkaXN0KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiZ3JleTc4IiwgZmlsbD1OQSkpCgoKYGBgCgoKIyMjIyA5LjMgQ29tcGFyZWQgdG8gY291bnRlcmZhY3R1YWwKCmBgYHtyfQoKY291bnRlcmZhY3RfYWdlX3NleCA8LQogICAgICBhZGRfZXByZWRfZHJhd3Mob2JqZWN0ID0gbV9hZ2Vfc2V4LAogICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IG1kYXRhX2FnZV9zZXggJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCh5ZWFyLCB5ZWFyMiwgeV9udW0sIGFnZSwgc2V4KSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKGFjZl9wZXJpb2QgPSAiYS4gcHJlLWFjZiIpKSAlPiUKICAgICAgZmlsdGVyKHllYXI+MTk1NykgJT4lCiAgICAgIHNlbGVjdCh5ZWFyLCBhZ2UsIHNleCwgLmRyYXcsIC5lcHJlZF9jb3VudGVyZiA9IC5lcHJlZCkKICAKI0NhbGN1YXRlIHByZWRpY3RlZCBudW1iZXIgb2YgY2FzZXMgcGVyIGRyYXcsIHRoZW4gc3VtbWFyaXNlLgpwb3N0X2NoYW5nZV9hZ2Vfc2V4IDwtCiAgICAgIGFkZF9lcHJlZF9kcmF3cyhvYmplY3QgPSBtX2FnZV9zZXgsCiAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gbWRhdGFfYWdlX3NleCAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHllYXIsIHllYXIyLCB5X251bSwgYWdlLCBzZXgsIGFjZl9wZXJpb2QpKSAlPiUKICAgICAgZmlsdGVyKHllYXI+MTk1NykgJT4lCiAgICAgIHVuZ3JvdXAoKSAlPiUKICAgICAgc2VsZWN0KHllYXIsIGFnZSwgc2V4LCAuZHJhdywgLmVwcmVkKSAKICAKI2ZvciB0aGUgb3ZlcmFsbCBwZXJpb2QKY291bnRlcmZhY3Rfb3ZlcmFsbF9hZ2Vfc2V4IDwtCiAgICAgIGFkZF9lcHJlZF9kcmF3cyhvYmplY3QgPSBtX2FnZV9zZXgsCiAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gbWRhdGFfYWdlX3NleCAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHllYXIsIHllYXIyLCB5X251bSwgYWdlLCBzZXgpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUoYWNmX3BlcmlvZCA9ICJhLiBwcmUtYWNmIikpICU+JQogICAgICBmaWx0ZXIoeWVhcj4xOTU3KSAlPiUKICAgICAgc2VsZWN0KGFnZSwgc2V4LCAuZHJhdywgLmVwcmVkKSAgJT4lCiAgICAgIGdyb3VwX2J5KGFnZSwgc2V4LCAuZHJhdykgJT4lCiAgICAgIHN1bW1hcmlzZSguZXByZWRfY291bnRlcmYgPSBzdW0oLmVwcmVkKSkgJT4lCiAgICAgIG11dGF0ZSh5ZWFyID0gIk92ZXJhbGwgKDE5NTgtMTk2MykiKQogIAojQ2FsY3VhdGUgaW5jaWRlbmNlIHBlciBkcmF3LCB0aGVuIHN1bW1hcmlzZS4KcG9zdF9jaGFuZ2Vfb3ZlcmFsbF9hZ2Vfc2V4IDwtCiAgICAgIGFkZF9lcHJlZF9kcmF3cyhvYmplY3QgPSBtX2FnZV9zZXgsCiAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gbWRhdGFfYWdlX3NleCAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHllYXIsIHllYXIyLCB5X251bSwgYWdlLCBzZXgsIGFjZl9wZXJpb2QpKSAlPiUKICAgICAgZmlsdGVyKHllYXI+MTk1NykgJT4lCiAgICAgIHNlbGVjdChhZ2UsIHNleCwgLmRyYXcsIC5lcHJlZCkgJT4lCiAgICAgIGdyb3VwX2J5KC5kcmF3LCBhZ2UsIHNleCkgJT4lCiAgICAgIHN1bW1hcmlzZSguZXByZWQgPSBzdW0oLmVwcmVkKSkgCiAgCmNvdW50ZXJfcG9zdF9vdmVyYWxsX2FnZV9zZXggPC0KICBsZWZ0X2pvaW4oY291bnRlcmZhY3Rfb3ZlcmFsbF9hZ2Vfc2V4LCBwb3N0X2NoYW5nZV9vdmVyYWxsX2FnZV9zZXgpICU+JQogICAgbXV0YXRlKGNhc2VzX2F2ZXJ0ZWQgPSAuZXByZWRfY291bnRlcmYtLmVwcmVkLAogICAgICAgICAgIHBjdF9jaGFuZ2UgPSAoLmVwcmVkIC0gLmVwcmVkX2NvdW50ZXJmKS8uZXByZWRfY291bnRlcmYpICU+JQogICAgZ3JvdXBfYnkoYWdlLCBzZXgpICU+JQogICAgbWVhbl9xaShjYXNlc19hdmVydGVkLCBwY3RfY2hhbmdlKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIG11dGF0ZSh5ZWFyID0gIk92ZXJhbGwgKDE5NTgtMTk2MykiKSAKCgphZ2Vfc2V4X3R4dCA8LSBjb3VudGVyX3Bvc3Rfb3ZlcmFsbF9hZ2Vfc2V4ICU+JQogIG11dGF0ZShhY3Jvc3MoYyhjYXNlc19hdmVydGVkOmNhc2VzX2F2ZXJ0ZWQudXBwZXIpLCBudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4xLCBiaWcubWFyayA9ICIsIikpKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMocGN0X2NoYW5nZTpwY3RfY2hhbmdlLnVwcGVyKSwgcGVyY2VudCwgYWNjdXJhY3k9MC4xKSkgJT4lCiAgdHJhbnNtdXRlKHllYXIgPSBhcy5jaGFyYWN0ZXIoeWVhciksCiAgICAgICAgICAgIHNleCA9IHNleCwKICAgICAgICAgICAgYWdlID0gYWdlLAogICAgICAgICAgICBjYXNlc19hdmVydGVkID0gZ2x1ZTo6Z2x1ZSgie2Nhc2VzX2F2ZXJ0ZWR9XG4oe2Nhc2VzX2F2ZXJ0ZWQubG93ZXJ9IHRvIHtjYXNlc19hdmVydGVkLnVwcGVyfSkiKSwKICAgICAgICAgICAgcGN0X2NoYW5nZSA9IGdsdWU6OmdsdWUoIntwY3RfY2hhbmdlfVxuKHtwY3RfY2hhbmdlLmxvd2VyfSB0byB7cGN0X2NoYW5nZS51cHBlcn0pIikpCgoKYWdlX3NleF90eHQgJT4lIGRhdGF0YWJsZSgpCgoKYGBgCgpgYGB7cn0KCmNvdW50ZXJmYWN0dWFsX2dfYWdlX3NleCA8LSBjb3VudGVyX3Bvc3Rfb3ZlcmFsbF9hZ2Vfc2V4ICU+JSAKICBtdXRhdGUoc2V4ID0gY2FzZV93aGVuKHNleD09Ik0iIH4gIk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgc2V4PT0iRiIgfiAiRmVtYWxlIikpICU+JQogIG11dGF0ZShhZ2UgPSBjYXNlX3doZW4oYWdlPT0iMDBfMDUiIH4gIjAgdG8gNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMDZfMTUiIH4gIjA2IHRvIDE1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSIxNl8yNSIgfiAiMTYgdG8gMjV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjI2XzM1IiB+ICIyNiB0byAzNXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iMzZfNDUiIH4gIjM2IHRvIDQ1eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9PSI0Nl81NSIgfiAiNDYgdG8gNTV5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFnZT09IjU2XzY1IiB+ICI1NiB0byA2NXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgYWdlPT0iNjUrIiB+ICI2NSAmIHVwIHkiKSkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fcG9pbnRyYW5nZShhZXMoeCA9IGFnZSwgeT1jYXNlc19hdmVydGVkLCB5bWluPWNhc2VzX2F2ZXJ0ZWQubG93ZXIsIHltYXg9Y2FzZXNfYXZlcnRlZC51cHBlciwgY29sb3VyPXNleCwgc2hhcGU9c2V4KSwgcG9zaXRpb249cG9zaXRpb25fZG9kZ2Uod2lkdGg9MC41KSkgKyAKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoIiNDRDdBQzUiLCAiY2FkZXRibHVlMyIpLCBuYW1lPSIiKSArCiAgc2NhbGVfc2hhcGUobmFtZT0iIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGxhYnMoeD0iIiwKICAgICAgIHk9Ik51bWJlciAoOTUlIFVJKSIsCiAgICAgICBjb2xvdXI9IiIpICsKICB0aGVtZV9nZ2Rpc3QoKSArCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5NzgiLCBmaWxsPU5BKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCmNvdW50ZXJmYWN0dWFsX2dfYWdlX3NleApgYGAKCkpvaW4gdG9nZXRoZXIgZm9yIEZpZ3VyZSAzLgoKCmBgYHtyfQoKKHBlYWtfZ19hZ2Vfc2V4ICsgbGV2ZWxfZ19hZ2Vfc2V4KSAvIChzbG9wZV9nX2FnZV9zZXggKyBjb3VudGVyZmFjdHVhbF9nX2FnZV9zZXgpICsgcGxvdF9hbm5vdGF0aW9uKHRhZ19sZXZlbHMgPSAiQSIpICsgcGxvdF9sYXlvdXQoZ3VpZGVzID0gImNvbGxlY3QiKSAmIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKZ2dzYXZlKGhlcmUoImZpZ3VyZXMvZjMucG5nIiksIHdpZHRoID0gMTIsIGhlaWdodD04KQoKCmBgYAo=